﻿#region Imports
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using BulkRenamer.App.Main;
using BulkRenamer.Common.ResourceFiles;
using BulkRenamer.Common.Models;
using BulkRenamer.Sampler;
using BulkRenamer.Util;
using BulkRenamer.Util.Attributes;
using BulkRenamer.Util.Enums;
using CommonUtilities.Controls;
using CommonUtilities.Enums;
using CommonUtilities.Extensions;
using CommonUtilities.Info;
using CommonUtilities.Messages;
using CommonUtilities.Properties;
using CommonUtilities.Util;
using Microsoft.VisualBasic;
#endregion

namespace BulkRenamer.App.Views
{
    [Description("Trial_desc")]
    [FriendlyName("fr_name", Description = "")]
    public partial class frmBulkRenamer : Form
    {
        #region Declarations
        object lastSelection = "";
        string sDefaultPath = "";
        int suggLPatternLen = 0;
        int suggRPatternLen = 0;
        int maxBatchDescUpdateCount = 3;

        /// <summary>
        /// &quot;Debug mode&quot;
        /// </summary>
        public const string sDebugMode = "Debug mode";

        /// <summary>
        /// &quot;Successful&quot;
        /// </summary>
        public const string sSuccessful = "Successful";

        //const string sCheckBoxNamePrefix = "chkExtnFilter"; // RKD

        /// <summary>
        /// Wildcard character for search
        /// </summary>
        const char chrWildcard = '%';

        Dictionary<string, int> dictTrimChange = new Dictionary<string, int>();
        const string KEY_LTrimChange = "LTrimChange";
        const string KEY_RTrimChange = "RTrimChange";

        enum eExportFileExtn
        {
            CSV, XLS, TXT
        }

        [DefaultValue(eFileUpdateMode.Name)]
        enum eFileUpdateMode
        {
            Name = 0,
            Attribute = 1
        }

        Form frmExportExtnChooser;
        private bool isMidClicked = false;

        const string sRestore = "&Restore";
        const string sMinToTray = "&Minimize to Tray";
        const string sUpdateFiles = "Update &files";
        #endregion

        #region Constructors
        /// <summary>
        /// Default constructor
        /// </summary>
        public frmBulkRenamer()
        {
            #region Test methods RKD
            //List<string> str = "s01e02 - A Big Cat".ToWords().ToList();
            //bool bln = CheckFileNameFormatForSeparator(str.First());

            //MusicID3Tag mp3 = MusicID3Tag.ReadFile(@"M:\Tagore Quintet\Tomar Holo Shuru.mp3");
            //MessageBox.Show(mp3.Title);

            //ObservableCollection<FileDetails> x1 = new ObservableCollection<FileDetails>();
            //x1 = x1.AddItem(FileDetails.CreateItem("ABC", false, "")).AddItem(FileDetails.CreateItem("DEF", false, "")).AddItem(FileDetails.CreateItem("GHI", false, "")).ToObservableCollection();
            //x1.AddCollectionIndex("FileId");
            //ObservableCollection<int> i1 = "1,2".Split(',').Select(val => Convert.ToInt32(val)).ToObservableCollection();
            //x1.RemoveItems(x1.Where(fdet => i1.Contains(fdet.FileId)));
            //x1 = x1.RemoveItems(x1.Where(fdet => i1.Contains(fdet.FileId))).ToObservableCollection();

            // AllFileDetails = AllFileDetails.AddItem(new FileDetails()).AddItem(new FileDetails()).ToObservableCollection();
            // eAllowance z1 = (eAllowance)EnumExtensions.EnumFromInt<eAllowance>(9999);
            // EnumWithNoDefaultValue.eNoDefVal z2 = EnumExtensions.GetDefaultValue<EnumWithNoDefaultValue.eNoDefVal>();
            // eAllowance z2 = (eAllowance)s3; 
            #endregion

            InitialLoadComplete = false;
            //AllFileDetails.CollectionChanged += new NotifyCollectionChangedEventHandler(AllFileDetails_CollectionChanged);

            // http://stackoverflow.com/questions/2797084/using-custom-cursor-winforms
            // http://msdn.microsoft.com/en-us/library/system.windows.forms.cursor%28v=VS.80%29.aspx
            // http://stackoverflow.com/questions/4305800/using-custom-colored-cursors-in-a-c-sharp-windows-application
            // http://stackoverflow.com/questions/489791/how-do-you-use-a-custom-mouse-cursor-in-a-net-application
            // http://tech.pro/tutorial/732/csharp-tutorial-how-to-use-custom-cursors
            //btnLoadExtn.Cursor = new Cursor(GetType(), "dex.cur");  // new Cursor(Application.StartupPath + @"\Images\dex.cur");
            //btnLoadExtn.Cursor = Utilities.LoadCustomCursor(Application.StartupPath + @"\Images\dex.cur");
            //btnLoadExtn.Cursor = new Cursor(Image.FromFile(Application.StartupPath + @"\Images\dex.cur"));

            if (Program.IsDebug)
            {
                MessageBox.Show("Debug LOG", "LOG: new design");
                MessageBox.Show("http://www.codeproject.com/Articles/14744/Read-Write-App-Config-File-with-NET-2-0", "Settings in App.config");
                MessageBox.Show("http://www.codeproject.com/Articles/27849/WaterMark-TextBox-For-Desktop-Applications-Using-C", "Watermark");
            }

            BRUtilities.SetKeyLock(eLockKey.NumLock, true);
            InitializeComponent();
            this.Text = AppInfo.ApplicationTitle;

            // Control initializations
            mnuUpdateFiles.Text = btnUpdateFiles.Text = sUpdateFiles;
            cmbUpdateType.PopulateComboBoxWithEnum<eFileUpdateMode>(selection: eFileUpdateMode.Name, isSortAscending: true);
            chkAddSeparator.Checked = true;

            // Variables initializations
            sDefaultPath = Application.StartupPath;
            dictTrimChange.Add(KEY_LTrimChange, 0);
            dictTrimChange.Add(KEY_RTrimChange, 0);

            // Events initializations
            // ???

            // Collection initializations
            AllPathHistory = new ObservableCollection<PathHistory>();
            AllDeletedIds = new ObservableCollection<FileDeletionDetails>();
            AllExportExtn = IdNameValueMember.GetMembersFromSettings(Program.BulkRenamerSettings["ExportExtensions"]);
            AllFileExtenstions = IdNameValueMember.GetMembersFromSettings(Program.BulkRenamerSettings["FileExtensions"]);
        }
        #endregion

        #region Properties
        /// <summary>
        /// Specifies if in initial load phase
        /// </summary>
        public bool InitialLoadComplete { get; private set; }

        /// <summary>
        /// All file extensions
        /// </summary>
        public ObservableCollection<IdNameValueMember> AllFileExtenstions { get; private set; }

        public ObservableCollection<IdNameValueMember> AllExportExtn { get; private set; }

        /// <summary>
        /// Path for selected log file
        /// </summary>
        public string SelectedLogFile { get; private set; }

        /// <summary>
        /// Path for file selection
        /// </summary>
        public string SelectedBasePath { get; private set; }

        /// <summary>
        /// Extn. selected for loading files
        /// </summary>
        public string SelectedExtn { get; private set; }

        /// <summary>
        /// Selected file for Bulk export/import
        /// </summary>
        public string SelectedExportFile { get; private set; }

        private ObservableCollection<FileDetails> allFileDetails = new ObservableCollection<FileDetails>();
        /// <summary>
        /// All file details
        /// </summary>
        //public ObservableCollection<FileDetails> AllFileDetails { get; set; }
        public ObservableCollection<FileDetails> AllFileDetails
        {
            get { return allFileDetails; }
            private set { allFileDetails = (!value.IsNull() ? value : new ObservableCollection<FileDetails>()); }
        }

        /// <summary>
        /// Information on files deleted (soft delete) from the File list
        /// </summary>
        public ObservableCollection<FileDeletionDetails> AllDeletedIds { get; private set; }

        /// <summary>
        /// File details selected in editor
        /// </summary>
        public FileDetails SelectedFileDetails { get; private set; }

        /// <summary>
        /// Maximum allowed trim length
        /// </summary>
        public int MaxTrimRange { get; private set; }

        /// <summary>
        /// Trim length of file name from left
        /// </summary>
        public int custLTrimLength { get; private set; }

        /// <summary>
        /// Trim length of file name from right
        /// </summary>
        public int custRTrimLength { get; private set; }

        /// <summary>
        /// Selected extn. for exporting file in Bulk mode
        /// </summary>
        public string SelectedExportExtn { get; private set; }

        /// <summary>
        /// Filters on multi-extn. selection mode
        /// </summary>
        public ObservableCollection<ExtnFilters> AllFilters { get; private set; }

        /// <summary>
        /// Path suggestions based on text entered
        /// </summary>
        public ObservableCollection<IdNameValueMember> AllPathSuggestions { get; private set; }

        /// <summary>
        /// Specifies if currently navigating Base Path using Up/Down/Prev/Next buttons
        /// </summary>
        public bool IsNavigating { get; private set; }

        /// <summary>
        /// Specifies if prompt for Log file selection should be ignored during a batch
        /// </summary>
        public bool IgnoreLogPrompt { get; private set; }

        /// <summary>
        /// Specifies if ComboBox-AddressTextBox combo or TextBox.AutoCompleteSource is used for path suggestion
        /// </summary>
        public bool IsUsingComboText
        {
            get { return (bool)Program.BulkRenamerSettings["UseComboText"]; }
        }

        private bool SupressSuggestion { get; set; }

        private ObservableCollection<PathHistory> AllPathHistory { get; set; }

        public bool AllowAutoCopy_Prompt { get; private set; }

        private int suggestCount;
        public int SuggestCount
        {
            get { return suggestCount; }
            private set
            {
                suggestCount = value;

                numLTrim.Enabled = numRTrim.Enabled = (suggestCount > 0);
                if (suggestCount == 0)
                {
                    SupressSuggestion = true;
                    numLTrim.Value = numRTrim.Value = custLTrimLength = custRTrimLength = 0;
                    SupressSuggestion = false;
                }
            }
        }

        public string DeleteButtonText { get; set; }

        private string lastBatchStamp;
        public string LastBatchStamp
        {
            get { return lastBatchStamp; }
            private set
            {
                lastBatchStamp = value;

                mnuRevertLastBatch.Enabled = !lastBatchStamp.IsNullOrEmpty();
                if (Program.IsDebug) mnuRevertLastBatch.Enabled = true;
            }
        }

        public string LastBatchStamp_bup { get; private set; }

        public bool IsReloading { get; private set; }

        public bool IsInit { get; private set; }

        private eFileUpdateMode SelectedFileUpdateMode { get; set; }
        #endregion

        #region Events
        /// <summary>
        /// Form Load event
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void frmMain_Load(object sender, EventArgs e)
        {
            InitializationControls();
            InitialLoadComplete = true;
            SuggestCount = 0;
            SelectedFileUpdateMode = 0;
        }

        private void chkAnyExtn_CheckedChanged(object sender, EventArgs e)
        {
            if (chkAnyExtn.Checked) lastSelection = SelectedExtn;

            cmbExtnSelector.Enabled = !chkAnyExtn.Checked;
            cmbExtnFilters.Enabled = chkExtnFilterSelMode.Enabled = chkAnyExtn.Checked;

            if (cmbExtnSelector.SelectedValue == lastSelection) btnLoadExtn_Click(sender, e);
        }
        #endregion

        #region Methods
        /// <summary>
        /// Initializes all controls
        /// </summary>
        private void InitializationControls()
        {
            IsInit = true;

            LastBatchStamp = "";

            AllFilters = new ObservableCollection<ExtnFilters>();
            AllFileDetails = new ObservableCollection<FileDetails>();
            AllDeletedIds = new ObservableCollection<FileDeletionDetails>();

            if (Program.IsDebug) MessageBox.Show("Look for: RKD_1234\r\nAdd comments\r\nSet Program.IsDebug = false;\r\n\r\nMenu properties=control properties e.g. Enabled etc", "TO DO");
            if (Program.IsDebug) { mnuRevertLastBatch.Enabled = true; mnuRevertLastBatch.Text += " (Dbg: enabld alwz)"; }

            this.Cursor = Cursors.Arrow;
            mnuMinimize.Visible = (this.FormBorderStyle == FormBorderStyle.None);
            txtSelCount.BackColor = txtRecCount.BackColor = this.BackColor;

            ntfySysTray.Visible = false;
            ntfySysTray.Text = AppInfo.AssemblyTitle;
            ntfySysTray.Icon = this.Icon; // new Icon(SystemIcons.Application, 40, 40);

            cmnuMinRestore.Text = isMinToTray ? sRestore : sMinToTray;
            mnuAboutApp.ToolTipText = mnuAboutApp.Text = "About " + AppInfo.AssemblyTitle;
            mnuPreviewAll.ToolTipText = tltpMain.GetToolTip(picGridPreview);
            mnuSuggestNames.ToolTipText = tltpMain.GetToolTip(btnSuggestNames);
            tltpMain.SetToolTip(txtAppend, ResourceBulkRenamer.ToolTip_Append.FormatTextFromParams(Utilities.cDelim));
            tltpMain.SetToolTip(txtReplace, ResourceBulkRenamer.ToolTip_Replace.FormatTextFromParams(Utilities.cDelim));

            txtBasePath.Visible = cmbPathSuggestion.Visible = IsUsingComboText;
            txtBasePath_AutoSugg.Visible = !IsUsingComboText;

            btnUpdateFiles.Anchor = grpOutput.Anchor;
            chkAutoLoad.Checked = true;
            chkAnyExtn.Checked = false;
            chkMid.CheckState = CheckState.Unchecked;
            txtLogPath.Text = sDefaultPath + @"\Logs\BulkRenamer_log.csv";
            txtBasePath_AutoSugg.Text = txtBasePath.Text = "";
            txtExtnFilters.BorderStyle = BorderStyle.None;
            mnuFetchDirDown.Enabled = mnuFetchDirUp.Enabled = mnuFetchDirPrev.Enabled = mnuFetchDirNext.Enabled = btnFetchDirDown.Enabled = btnFetchDirUp.Enabled = btnFetchDirPrev.Enabled = btnFetchDirNext.Enabled = btnLoadFiles.Enabled = !SelectedBasePath.IsNullOrEmpty();
            txtExtnFilters.Enabled = cmbExtnFilters.Enabled = chkExtnFilterSelMode.Enabled = chkAnyExtn.Checked;
            //EventList.ToggleDropDown(cmbPathSuggestion, EventList.eShowHideType.Show);
            grpOutput.Enabled = false;

            BRUtilities.CopyDesign(txtBasePath, txtBasePath_AutoSugg, false);
            BRUtilities.CopyDesign(txtBasePath, cmbPathSuggestion, false);
            BRUtilities.CopyDesign(cmbExtnFilters, txtExtnFilters, true);
            //if (Program.IsDebug) { cmbPathSuggestion.Top = txtBasePath.Top + 10; cmbPathSuggestion.Left = txtBasePath.Left + 10; } // RKD: remove

            /* RKD :: very imp. piece
            // Add File-menu items to context menu
            List<ToolStripItem> lstFileMenuItems = mnuFiles.DropDownItems.Cast<ToolStripItem>().ToList();
            if (!(ctxtmnuFileNames.Items.Count > 0).IsNull())
                lstFileMenuItems._ForEach(itm => itm.Font = ctxtmnuFileNames.Items[0].Font);
            mnuFiles.Enabled = (mnuFiles.DropDownItems.Count == 0);
            ctxtmnuFileNames.Items.Clear();
            ctxtmnuFileNames.Items.AddRange(lstFileMenuItems.ToArray());

            // Add BulkRenamer-menu items to SystemTray-icon context menu
            //ctxtmnuSysTray.Items.Clear();
            //ToolStripMenuItem x = new ToolStripMenuItem() { Name = "mnuRestore", Text = "&Restore", Font = mnuBulkRenamer.DropDownItems[0].Font };
            //x.Click += mnuMinimizeToTray_Click;
            //ctxtmnuSysTray.Items.AddRange(new ToolStripItem[] { x });
            // //ctxtmnuSysTray.Items.AddRange(mnuBulkRenamer.DropDownItems.Cast<ToolStripItem>().ToArray());

            // //ctxtmnuSysTray.Items.AddRange(Utilities.CopyValues<BRToolStripItem>(mnuBulkRenamer.DropDownItems.Cast<BRToolStripItem>().AfterItems(mnuRefresh, true)).ToArray());
            // //ObservableCollection<BRToolStripItem> z = mnuBulkRenamer.DropDownItems.Cast<ToolStripItem>().AfterItems(mnuRefresh, true).Cast<BRToolStripItem>().ToObservableCollection();
            //ctxtmnuSysTray.Items.AddRange(mnuBulkRenamer.DropDownItems.Cast<ToolStripItem>().AfterItems(mnuRefresh, true).ToArray());
            foreach (ToolStripItem item in mnuBulkRenamer.DropDownItems.Cast<ToolStripItem>().AfterItems(mnuSep01, true))
                ctxtmnuSysTray.Items.Add(item);
            ntfySysTray.ContextMenuStrip = ctxtmnuSysTray;
            
            List<ToolStripItem> z = new List<ToolStripItem>(mnuBulkRenamer.DropDownItems.Cast<ToolStripItem>().SkipTill(mnuRefresh, true));
            ctxtmnuSysTray.Items.AddRange(z.ToArray());
            ntfySysTray.ContextMenuStrip = ctxtmnuSysTray;
           */

            // Images
            mnuBulkRenamer.Image = mnuAboutApp.Image = ResourceBulkRenamer.Img_BulkRenamer;
            mnuRefresh.Image = ResourceBulkRenamer.Img_Refresh;
            cmnuQuit.Image = mnuQuit.Image = ResourceBulkRenamer.Img_Quit;
            mnuMinimizeToTray.Image = ResourceBulkRenamer.Img_Tray_Minimize;
            mnuUpdateFiles.Image = ResourceBulkRenamer.Img_Save;
            mnuRevertUpdate.Image = ResourceBulkRenamer.Img_Undo;
            mnuUsersGuide.Image = ResourceBulkRenamer.Img_Help;
            mnuSuggestNames.Image = ResourceBulkRenamer.Img_Suggest;
            mnuDirection.Image = ResourceBulkRenamer.Img_Direction_All;
            mnuFetchDirUp.Image = Resources.Direction_Up;
            mnuFetchDirDown.Image = Resources.Direction_Down;
            mnuFetchDirPrev.Image = Resources.Direction_Left;
            mnuFetchDirNext.Image = Resources.Direction_Right;

            RefreshCount();

            cmbExtnSelector.DropDownStyle = ComboBoxStyle.DropDownList;
            cmbExtnSelector.DataSource = AllFileExtenstions;
            cmbExtnSelector.ValueMember = "Name";
            cmbExtnSelector.DisplayMember = "Value";
            cmbExtnSelector.SelectedIndex = 0;

            // Tooltips
            tltpMain.SetToolTip(lblBasePath, "Open folder in Windows Default");

            // Event calling
            btnLoadExtn_Click(null, null);

            string basePath = Application.StartupPath;
            string logPath = Directory.GetParent(Application.StartupPath) + @"\Logs\BulkRenamer_log.csv";
            if (!Program.IsDebug)
            {
                string clipbrdPath = Clipboard.ContainsText(TextDataFormat.Text) || Clipboard.ContainsText(TextDataFormat.UnicodeText) ? Clipboard.GetText() : "";
                if (clipbrdPath.ValidatePath(ePathValidation.Directory))
                    if (MessageBoxUtil.Show(Program.IsDebug, ResourceBulkRenamer.Msg_BasePathInit.FormatTextFromParams(clipbrdPath), ResourceBulkRenamer.App_BasePathInit, MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
                        basePath = Clipboard.GetText();
            }
            else if (Program.IsDebug)
            {
                basePath = @"C:\Documents and Settings\rhythm divine\My Documents\Downloads\Ant Videos"; // Application.StartupPath
                // basePath = @"Z:\Friends Complete Seasons 1-10 Uncut DVDRip - 480p\Friends Season 7"; // RKD: kol test
                logPath = Directory.GetParent(Application.StartupPath) + @"\Logs\log_20130512_024710.csv"; // RKD: kol test
                basePath = @"J:\[TV series]\Alfred.Hitchcock.Presents\Alfred Hitchcock Presents Season 1"; // RKD: gnagr test
                basePath = @"S:\_series\DOWN\House of Cards\House.Of.Cards.2013.S01.Season.1.720p.BluRay.x264.anoXmous";
            }

            chkAnyExtn.Checked = true;
            txtLogPath.Text = logPath;
            if (IsUsingComboText) txtBasePath.Text = basePath;
            else txtBasePath_AutoSugg.Text = basePath;

            if (IsUsingComboText) cmbPathSuggestion.DroppedDown = false;
            DeleteButtonText = btnDelete.Text;

            IsInit = false;
        }
        #endregion

        #region :: System tray
        private bool isMinToTray = false;
        public bool IsMinToTray
        {
            get { return isMinToTray; }
            set
            {
                isMinToTray = value;

                this.ShowInTaskbar = this.Visible = !isMinToTray;
                ntfySysTray.Visible = mnuShowInTray.Checked || isMinToTray;

                if (isMinToTray) ntfySysTray.ShowBalloonTip(2500, AppInfo.AssemblyTitle, ResourceBulkRenamer.Msg_AppInBackgorund.FormatTextFromParams(AppInfo.AssemblyTitle), ToolTipIcon.Info);
                cmnuMinRestore.Text = isMinToTray ? sRestore : sMinToTray;
                cmnuMinRestore.Image = isMinToTray ? ResourceBulkRenamer.Img_Tray_Restore : ResourceBulkRenamer.Img_Tray_Minimize;

                //if (isMinToTray)
                //    ctxtmnuSysTray.Items.AddRange(mnuBulkRenamer.DropDownItems);
                //else
                //    mnuBulkRenamer.DropDownItems.AddRange(ctxtmnuSysTray.Items);
            }
        }

        public void MinimizeToTray()
        {
            IsMinToTray = !IsMinToTray;
        }

        private void mnuMinimize_Click(object sender, EventArgs e)
        {
            this.WindowState = FormWindowState.Minimized;
        }

        private void mnuMinimizeToTray_Click(object sender, EventArgs e)
        {
            MinimizeToTray();
        }

        private void ntfySysTray_MouseClick(object sender, MouseEventArgs e)
        {
            switch (e.Button)
            {
                case MouseButtons.Left:
                    break;
                case MouseButtons.Middle:
                    break;
                case MouseButtons.None:
                    break;
                case MouseButtons.Right:
                    ctxtmnuSysTray.Show();
                    break;
                case MouseButtons.XButton1:
                    break;
                case MouseButtons.XButton2:
                    break;
                default:
                    break;
            }
        }

        private void ntfySysTray_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            switch (e.Button)
            {
                case MouseButtons.Left:
                    IsMinToTray = !IsMinToTray;
                    break;
                case MouseButtons.Middle:
                    break;
                case MouseButtons.None:
                    break;
                case MouseButtons.Right:
                    break;
                case MouseButtons.XButton1:
                    break;
                case MouseButtons.XButton2:
                    break;
                default:
                    break;
            }

        }
        #endregion

        private void btnBrowseLogFile_Click(object sender, EventArgs e)
        {
            OpenFileDialog dlgOpenLogFile = null;
            if (BrowseLogFile(ref dlgOpenLogFile) == DialogResult.OK)
            {
                txtLogPath.Text = dlgOpenLogFile.FileName;
                if (!Directory.Exists(Path.GetDirectoryName(dlgOpenLogFile.FileName)))
                    Directory.CreateDirectory(Path.GetDirectoryName(dlgOpenLogFile.FileName));
                if (!File.Exists(dlgOpenLogFile.FileName))
                    Utilities.CreateFile(SelectedExportFile);
            }
        }

        private void btnBrowseBasePath_Click(object sender, EventArgs e)
        {
            if (Form.ModifierKeys == Keys.Control)
            {
                frmPathHistorySelector pathHistSelector = new frmPathHistorySelector(AllPathHistory);
                pathHistSelector.ShowDialog(this);
                if (pathHistSelector.DialogResult == DialogResult.OK) LoadPath(sender, e, pathHistSelector.SelectedPath);

                pathHistSelector.Dispose();
                pathHistSelector = null;
                return;
            }

            /*
            // RKD :: USE for SendTo
            OpenFileDialog dlgOpenTargetFile = new OpenFileDialog
            {
                Title = "Select base directory..",
                Multiselect = true,
                CheckFileExists = true,
                CheckPathExists = true,
                RestoreDirectory = true
            };

            dlgOpenTargetFile.InitialDirectory = !Utilities.IsPathFormatValid(IsUsingComboText ? txtBasePath.Text : txtBasePath_AutoSugg.Text) ? sDefaultPath
                : Directory.Exists(IsUsingComboText ? txtBasePath.Text : txtBasePath_AutoSugg.Text) ? (IsUsingComboText ? txtBasePath.Text : txtBasePath_AutoSugg.Text) : sDefaultPath;

            string sDialogFilters = "";
            if (chkAnyExtn.Checked)
                sDialogFilters = FormatDialogFilters(new IdNameValueMember { Name = sAllExtn, Value = "*.*" });
            else
            {
                AllFileExtenstions.Where(idval => !((string)idval.Name).IsNullOrEmpty() & (string)idval.Name == SelectedExtn)
                    .OrderBy(idval => (int)idval.Id == 0 ? 1 : 0).ThenBy(idval => (int)idval.Id)
                    .ForEach(idval => sDialogFilters += FormatDialogFilters(idval));
            }

            dlgOpenTargetFile.Filter = sDialogFilters.TrimEnd('|');
            dlgOpenTargetFile.FilterIndex = !SelectedExtn.IsNullOrEmpty() || chkAnyExtn.Checked ? 1
                : dlgOpenTargetFile.Filter.Split('|').Select(flt => flt.RemoveExtnChars()).ToList().IndexOf(SelectedExtn) / 2 + 1;
            dlgOpenTargetFile.DefaultExt = chkAnyExtn.Checked ? "" : SelectedExtn;

            if (dlgOpenTargetFile.ShowDialog() == DialogResult.OK)
                LoadPath(sender, e, Path.GetDirectoryName(dlgOpenTargetFile.FileName));
            */

            chkAnyExtn.Enabled = cmbExtnFilters.Enabled = btnLoadExtn.Enabled = false;
            FolderBrowserDialog dlgFolderBrowser = new FolderBrowserDialog()
            {
                Description = "Select base directory..",
                ShowNewFolderButton = false
            };

            if (SelectedBasePath.ValidatePath(ePathValidation.Directory))
                dlgFolderBrowser.SelectedPath = SelectedBasePath;
            else dlgFolderBrowser.RootFolder = Environment.SpecialFolder.Desktop;

            if (dlgFolderBrowser.ShowDialog() == DialogResult.OK)
                LoadPath(sender, e, dlgFolderBrowser.SelectedPath);
        }

        private void LoadPath(object sender, EventArgs e, string basePath)
        {
            if (IsUsingComboText) txtBasePath.Text = basePath;
            else txtBasePath_AutoSugg.Text = basePath;
            if (chkAutoLoad.Checked) btnLoadFiles_Click(sender, e);
        }

        internal string FormatDialogFilters(IdNameValueMember idval)
        {
            return (!idval.Description.IsNullOrEmpty() ? idval.Description : idval.Name.ToString().ToUpper() + " files")
                + " (" + idval.Value + ")|" + idval.Value + "|";
        }

        private void btnLoadExtn_Click(object sender, EventArgs e)
        {
            if (SelectedExtn != (string)cmbExtnSelector.SelectedValue || chkAnyExtn.Checked) // selection changed
            {
                SelectedExtn = chkAnyExtn.Checked ? ExtnFilters.sAllExtn : (string)cmbExtnSelector.SelectedValue;
                btnLoadExtn.Text = "&Loaded " + SelectedExtn.ToUpper();

                IdNameValueMember idval = (IdNameValueMember)cmbExtnSelector.SelectedItem;
                tltpMain.SetToolTip(btnLoadExtn, chkAnyExtn.Checked
                    ? ResourceBulkRenamer.ToolTip_Extn_All.FormatTextFromParams(SelectedExtn.ToUpper())
                    : ResourceBulkRenamer.ToolTip_Extn_Loaded.FormatTextFromParams(SelectedExtn.ToUpper(), cmbExtnSelector.Text.ToUpper(), idval.Description));

                ClearList();
                ClearAll();

                if (chkAutoLoad.Checked) btnLoadFiles_Click(sender, e);
            }
        }

        private void ClearList()
        {
            lstFileNames.DataSource = AllFileDetails = new ObservableCollection<FileDetails>();
        }

        private void ClearAll()
        {
            ClearEditor();

            chkMid.Checked = false;
            grpOutput.Enabled = cmnuDeleteSelection.Enabled = lstFileNames.Enabled = btnDelete.Enabled = btnExport.Enabled = btnUpdateFiles.Enabled = GetValidFileList(false).Any();
            numLTrim.Value = numRTrim.Value = numLTrim.Maximum = numRTrim.Maximum = numLTrim.Minimum = numRTrim.Minimum = custLTrimLength = custRTrimLength = 0;
            txtPattern.Text = txtReplace.Text = txtAppend.Text = "";
            dictTrimChange[KEY_LTrimChange] = dictTrimChange[KEY_RTrimChange] = 0;
        }

        private void ClearEditor()
        {
            txtNameOrg.Text = txtNameSugg.Text = txtNameCustom.Text = txtExtn.Text = txtId.Text = "";
            chkAutoUpdate.Checked = radSuggested.Checked = true;
            grpEditor.Enabled = false;
        }

        private void btnLoadFiles_Click(object sender, EventArgs e)
        {
            SuggestCount = 0;
            lstFileNames.DataSource = AllFileDetails = new ObservableCollection<FileDetails>();
            cmnuDeleteSelection.Enabled = btnDelete.Enabled = btnExport.Enabled = false;
            grpEditor.Enabled = mnuSuggest.Enabled = grpOutput.Enabled = false;

            if (Directory.Exists(SelectedBasePath))
            {
                ObservableCollection<string> allFileNames = Utilities.GetValidFiles(SelectedBasePath)
                    .Where(fpath => chkAnyExtn.Checked || Path.GetExtension(fpath).TrimStart('.') == SelectedExtn)
                    .OrderBy(fpath => Path.GetFileName(fpath)).ToObservableCollection();

                ObservableCollection<FileDetails> allFileDetails = new ObservableCollection<FileDetails>();
                allFileNames.ForEach(fileName => allFileDetails.Add(FileDetails.CreateItem(fileName, chkAnyExtn.Checked, SelectedExtn)));

                allFileDetails = allFileDetails.AddCollectionIndex<FileDetails>("FileId").ToObservableCollection();
                AllFileDetails = allFileDetails.OrderBy(fdet => fdet.FileId).ToObservableCollection();

                if (AllDeletedIds.IsLinqIterable<FileDeletionDetails>(true)) AllDeletedIds.Clear();
                else AllDeletedIds = new ObservableCollection<FileDeletionDetails>();

                chkExtnFilterSelMode.ThreeState = false;
                chkExtnFilterSelMode.CheckState = CheckState.Checked;
                ReloadList(true);
                lstFileNames.DisplayMember = "NameOrg";
                lstFileNames.ValueMember = "NameOrg";

                ClearAll();
                DefineRange(null);

                ObservableCollection<FileDetails> validFiles = GetValidFileList(false);
                cmnuDeleteSelection.Enabled = lstFileNames.Enabled = btnDelete.Enabled = btnExport.Enabled = btnUpdateFiles.Enabled = validFiles.Any();
                mnuSuggest.Enabled = grpOutput.Enabled = (validFiles.Count() > 1);
                chkMid.Checked = false;

                grpOutput.Controls.Cast<Control>().ForEach(ctrl => ctrl.Enabled = (ctrl.Name != btnUpdateFiles.Name) ? grpOutput.Enabled : ctrl.Enabled);

                chkAddSeparator_CheckChanged(sender, e);

                if (validFiles.Any()) LoadFileDetails(0);
                if (AllFileDetails.Any()) AddPathToHistory();
            }
        }

        private void AddPathToHistory()
        {
            string path = SelectedBasePath.Trim(Path.DirectorySeparatorChar);
            if (AllPathHistory.Select(hist => hist.Path).Contains(path))
                AllPathHistory.Where(hist => hist.Path == path).First().LastAccessed = DateTime.Now;
            else
                AllPathHistory.Add(new PathHistory { Path = path });
        }

        private void UpdateMaxTrimRange()
        {
            MaxTrimRange = GetValidFileList(false).IsLinqIterable<FileDetails>(true) ? GetValidFileList(false).Select(fdet => fdet.NameOrg.Length).Min() - 1 : 0;
            DefineRange(null);
        }

        private void btnSuggestNames_Click(object sender, EventArgs e)
        {
            if (IsInit) return;
            string controlName = Utilities.GetControlName(sender);

            if (controlName == txtAppend.Name && txtAppend.IsWatermarkText) return;
            if (controlName == txtReplace.Name && txtReplace.IsWatermarkText) return;

            if (controlName == btnSuggestNames.Name) SuggestCount = 0;
            SuggestCount++;

            ObservableCollection<FileDetails> validFiles = GetValidFileList(false);
            if (!validFiles.IsLinqIterable<FileDetails>(true)) return;

            // Check pattern
            suggLPatternLen = 0;
            suggRPatternLen = 0;

            if (!chkMid.Checked)
            {
                if (!Utilities.GenerateFileNameSuggestion(validFiles.Select(file => file.NameOrg), ref suggLPatternLen, ref suggRPatternLen))
                    throw new InvalidOperationException("Suggestion failed!!");

                if (controlName == btnSuggestNames.Name)
                { SupressSuggestion = true; numRTrim.Value = numRTrim.Maximum = custRTrimLength = suggRPatternLen; SupressSuggestion = false; }
            }

            // Trim common pattern - suggestion temp
            int totLTrim = suggLPatternLen + custLTrimLength; // LTrim
            int totRTrim = custRTrimLength; // RTrim
            if (!chkMid.Checked)
                validFiles._ForEach(fdet => fdet.NameSuggTemp = fdet.NameOrg.Substring(totLTrim, fdet.NameOrg.Length - (totLTrim + custRTrimLength)).Trim('.')); // LTrim+RTrim
            else if (chkMid.Checked && custRTrimLength > 0)
                validFiles._ForEach(fdet => fdet.NameSuggTemp = fdet.NameOrg.Substring(totLTrim, totRTrim).Trim('.')); // Mid

            // Replace text
            string sReplace = txtReplace.Text;
            if (txtReplace.IsWatermarkText) sReplace = "";
            if (!sReplace.IsNullOrEmpty())
            {
                List<string> repl = sReplace.Split(Utilities.cDelim).ToList();
                if (Utilities.ValidateBetween(repl.Count(), 1, 2))
                    validFiles._ForEach(fdet => fdet.NameSuggTemp = fdet.NameSuggTemp.Replace(repl.First(), repl.Count() > 1 ? repl.Last() : "")); // Replace
            }

            // Append text
            string sAppend = txtAppend.Text;
            if (txtAppend.IsWatermarkText) sAppend = "";
            if (!sAppend.IsNullOrEmpty())
            {
                List<string> append = sAppend.Split(Utilities.cDelim).ToList();
                if (Utilities.ValidateBetween(append.Count(), 1, 2))
                    validFiles._ForEach(fdet => fdet.NameSuggTemp = append.First() + fdet.NameSuggTemp + (append.Count() > 1 ? append.Last() : "")); // Append
            }

            // Validate names + error messages
            ObservableCollection<string> errMsgs = new ObservableCollection<string>();
            if (validFiles.Where(fdet => fdet.NameSuggTemp == fdet.FileExtn).Any())
                errMsgs.Add(ResourceBulkRenamer.Err_FileName_OnlyExtn); // Only extn
            if (validFiles.Where(fdet => fdet.NameSuggTemp.IsNullOrEmpty()).Any())
                errMsgs.Add(validFiles.Where(fdet => fdet.NameSuggested.IsNullOrEmpty()).HasCount(0) ? ResourceBulkRenamer.Err_FileName_NoName_All : ResourceBulkRenamer.Err_FileName_NoName_Some); // Blank name
            if ((from fdet in validFiles group fdet by fdet.NameSuggTemp + "." + fdet.FileExtn into grp where !grp.Key.IsNullOrEmpty() && grp.Count() > 1 select grp.Key).Any())
                errMsgs.Add(ResourceBulkRenamer.Err_FileName_Duplicate); // Duplicate names

            if (errMsgs.Any())
            {
                bool isFirst = true;
                string msg = "Cannot suggest names!" + Utilities.cNewLine + "Reason" + (errMsgs.Count() > 1 ? "s" : "") + ": ";
                foreach (string err in errMsgs)
                { msg += (errMsgs.Count() > 1 ? (isFirst ? "" : "\t") + " " + (errMsgs.IndexOf(err) + 1) + ": " : "") + err + "\t" + Utilities.cNewLine; isFirst = false; }
                msg += Utilities.cNewLine + "Do you still want to continue with suggestions?\t";

                if (MessageBoxUtil.Show(Program.IsDebug, msg, ResourceBulkRenamer.App_Validate_Suggestion, MessageBoxButtons.YesNo) == DialogResult.No)
                {
                    numLTrim.Value = custLTrimLength - dictTrimChange[KEY_LTrimChange];
                    numRTrim.Value = custRTrimLength - dictTrimChange[KEY_RTrimChange];
                    return;
                }
            }

            // Suggested pattern
            if (chkMid.Checked)
            {
                txtPattern.Text = "Start: " + custLTrimLength + " | Len: " + custRTrimLength;
            }
            else
            {
                string sLTrim = "";
                string sRTrim = "";

                #region Pattern update
                /*
                IEnumerable<string> fileNames = validFiles.Select(fdet => fdet.NameOrg);
                sLTrim = Utilities.GetSuggestedPattern(fileNames, eTrimDirection.Left, suggLPatternLen);
                sRTrim = Utilities.GetSuggestedPattern(fileNames, eTrimDirection.Right, suggRPatternLen);
                txtPattern.Text = sLTrim + (!sRTrim.IsBlank() ? " | " + sRTrim : "");
                */

                //*
                // Suggested - auto
                if (suggLPatternLen > 0)
                {
                    string[] z = validFiles.Select(fdet => fdet.NameOrg.TrimAndTake(suggLPatternLen + (custLTrimLength < 0 ? custLTrimLength : 0), eTrimDirection.Left)).Distinct().ToArray();
                    if (z.HasCount(1)) sLTrim = "'" + z.First() + "'";
                }

                // LTrim - manual
                if (custLTrimLength > 0) sLTrim += " +" + custLTrimLength + "c";

                // RTrim - manual
                /*
                if (custRTrimLength > 0)
                {
                    string[] z = validFiles.Select(fdet => fdet.NameOrg.TrimAndTake(custRTrimLength, eTrimDirection.Right)).Distinct().ToArray();
                    if (z.HasCount(1)) sRTrim = "'" + z.First() + "'";
                    else sRTrim += "+" + custRTrimLength + "c";
                }
                */

                // Suggested - auto
                if (suggRPatternLen > 0)
                {
                    string[] z = validFiles.Select(fdet => fdet.NameOrg.TrimAndTake(custRTrimLength != suggRPatternLen ? custRTrimLength : suggRPatternLen, eTrimDirection.Right)).Distinct().ToArray();
                    sRTrim = "'" + z.First() + "'";
                }

                // RTrim - manual
                //if (custRTrimLength == 0 && suggRPatternLen > 0)
                //    custRTrimLength = suggRPatternLen;
                //else if (custRTrimLength > 0 && custRTrimLength != suggRPatternLen)
                //    sRTrim += " " + (custRTrimLength > suggRPatternLen ? "+" : "-") + Math.Abs(custRTrimLength - suggRPatternLen) + "c";
                //
                if (custRTrimLength != 0 && custRTrimLength != suggRPatternLen)
                    sRTrim += " " + (custRTrimLength > suggRPatternLen ? "+" : "-") + Math.Abs(custRTrimLength - suggRPatternLen) + "c";

                //txtPattern.Text = sLTrim + (custRTrimLength > 0 ? " | " + sRTrim : "");
                txtPattern.Text = sLTrim + (sRTrim.Length > 0 ? " | " + sRTrim : "");

                //txtPattern.Text = validFiles.Select(fdet => Utilities.Left(fdet.NameOrg, lenPattern + (custLTrimLength < 0 ? custLTrimLength : 0)) + (custLTrimLength > 0 ? " [+" + custLTrimLength + "]" : "") + (custRTrimLength > 0 ? " | " + Utilities.Right(fdet.NameOrg, custRTrimLength) : ""))
                //     .Distinct().First(); RKD
                //*/
                #endregion Pattern update
            }

            // Update Suggested and Custom names
            validFiles._ForEach(fdet =>
            {
                if (fdet.NameSuggested == fdet.NameCustom || fdet.NameCustom.IsNullOrEmpty())
                    fdet.NameCustom = fdet.NameSuggested = fdet.NameSuggTemp;
                else fdet.NameSuggested = fdet.NameSuggTemp;
            }); // update both, if same or Custom is empty; only Suggested, if different (always update Suggested)

            // Add TV Series episode index-name separator
            chkAddSeparator_CheckChanged(sender, e);

            if (validFiles.Any())
            {
                //if (!numLTrim.Enabled || !numRTrim.Enabled)
                //{
                txtReplace.Enabled = txtAppend.Enabled = txtPattern.Enabled = true;
                numLTrim.Minimum = suggLPatternLen * -1;
                //}

                LoadFileDetails(lstFileNames.SelectedIndex);
            }
        }

        private void chkAddSeparator_CheckChanged(object sender, EventArgs e)
        {
            if (!IsReloading && chkAddSeparator.Checked)
            {
                ObservableCollection<FileDetails> validFiles = GetValidFileList(false);
                validFiles._ForEach(fdet =>
                {
                    string nameSugg = fdet.NameSuggested;
                    fdet.NameSuggested = nameSugg.AddSeparator();

                    if (fdet.NameCustom == nameSugg) fdet.NameCustom = fdet.NameSuggested;
                    else fdet.NameCustom = fdet.NameCustom.AddSeparator();
                });

                LoadFileDetails(lstFileNames.SelectedIndex);
            }
        }

        private void lstFileNames_DoubleClick(object sender, EventArgs e)
        {
            if (Form.ModifierKeys == Keys.Control && !lstFileNames.SelectedItem.IsNull())
                Utilities.OpenPath(((FileDetails)lstFileNames.SelectedItem).FullPath);
            else
                LoadFileDetails(lstFileNames.SelectedIndex);
        }

        private void LoadFileDetails(int selectedFileIndex)
        {
            IsReloading = true;
            ObservableCollection<FileDetails> validFiles = GetValidFileList(false);
            if (validFiles.Any() && selectedFileIndex > -1)
            {
                grpEditor.Enabled = true;
                SelectedFileDetails = validFiles.ElementAt(selectedFileIndex);

                txtId.Text = SelectedFileDetails.FileId.ToString();
                txtNameOrg.Text = SelectedFileDetails.NameOrg;
                txtNameSugg.Text = SelectedFileDetails.NameSuggested;
                txtNameCustom.Text = SelectedFileDetails.NameCustom;
                txtExtn.Text = SelectedFileDetails.FileExtn;
                radSuggested.Checked = SelectedFileDetails.TakeSuggested;
                radCustom.Checked = !SelectedFileDetails.TakeSuggested;
                IsReloading = false;

                EvaluateManualFileDetailsUpdate();
            }
        }

        private void txtNameCustom_TextChanged(object sender, EventArgs e)
        {
            if (!IsReloading)
            {
                AllowAutoCopy_Prompt = false;
                radCustom.Checked = (txtNameSugg.Text != txtNameCustom.Text);
                radSuggested.Checked = !radCustom.Checked;
                AllowAutoCopy_Prompt = true;

                if (chkAutoUpdate.Checked) btnUpdateDetails_Click(sender, e);
            }

            EvaluateManualFileDetailsUpdate();




            /*
                if (e.KeyCode != Keys.ShiftKey && e.KeyCode != Keys.ControlKey && e.KeyCode != Keys.Alt)
                //if (Form.ModifierKeys != Keys.ControlKey && Form.ModifierKeys != Keys.ShiftKey && Form.ModifierKeys != Keys.Alt)
                {
                    //radCustom.Checked = (txtNameSugg.Text != txtNameCustom.Text);
                    //radSuggested.Checked = !radCustom.Checked;

                    radCustom.Checked = true;
                    radSuggested.Checked = !radCustom.Checked;
                }
            */
        }

        private void btnUpdateDetails_Click(object sender, EventArgs e)
        {
            if (chkMid.Checked && numRTrim.Value == 0) return;
            if (!ValidateNames(InitialLoadComplete)) return;

            SelectedFileDetails.NameSuggested = txtNameSugg.Text.Trim();
            SelectedFileDetails.NameCustom = txtNameCustom.Text.Trim();

            if (radSuggested.Checked) SelectedFileDetails.TakeSuggested = radSuggested.Checked;
            else if (radCustom.Checked) SelectedFileDetails.TakeSuggested = !radCustom.Checked;

            EvaluateManualFileDetailsUpdate();
        }

        private bool ValidateNames(bool confirmationPrompt, FileDetails selectedFileDetails = null)
        {
            if (!txtId.Text.IsNumeric()) return false;

            bool hasFileDetailForUpdate = !selectedFileDetails.IsNull();
            int _FileId = hasFileDetailForUpdate ? selectedFileDetails.FileId : Convert.ToInt32(txtId.Text);
            string _NameOrg = hasFileDetailForUpdate ? selectedFileDetails.NameOrg : txtNameOrg.Text;
            string _NameSuggested = hasFileDetailForUpdate ? selectedFileDetails.NameSuggested : txtNameSugg.Text;
            string _NameCustom = hasFileDetailForUpdate ? selectedFileDetails.NameCustom : txtNameCustom.Text;
            string _FileExtn = hasFileDetailForUpdate ? selectedFileDetails.FileExtn : txtExtn.Text;
            bool _TakeSuggested = hasFileDetailForUpdate ? selectedFileDetails.TakeSuggested : radSuggested.Checked;

            bool isValid = true;
            string EDITED_VAL = (_TakeSuggested ? _NameSuggested : _NameCustom);

            if (EDITED_VAL.IsNullOrEmpty())
            { if (confirmationPrompt) MessageBoxUtil.Show(Program.IsDebug, ResourceBulkRenamer.Msg_FileName_Blank); return false; }

            if (EDITED_VAL.ToLower() == _FileExtn)
            { if (confirmationPrompt)MessageBoxUtil.Show(Program.IsDebug, ResourceBulkRenamer.Msg_FileName_OnlyExtn); return false; }

            ObservableCollection<FileDetails> AllConflicts = GetValidFileList(false).Where(fdet => fdet.FileId != Convert.ToInt32(_FileId))
                .Where(fdet => (fdet.TakeSuggested ? fdet.NameSuggested : fdet.NameCustom) + "." + fdet.FileExtn == EDITED_VAL + "." + selectedFileDetails.FileExtn).ToObservableCollection();

            if (AllConflicts.HasCount(1))
            {
                FileDetails conflict = AllConflicts.First();
                DialogResult dlgres = DialogResult.None;

                if (confirmationPrompt)
                    dlgres = MessageBoxUtil.Show(Program.IsDebug, ResourceBulkRenamer.Msg_Validate_Duplicates.FormatTextFromParams(conflict.NameOrg, conflict.NameSuggested, conflict.NameCustom),
                        ResourceBulkRenamer.App_Validate_Duplicates, MessageBoxButtons.YesNo);

                if (confirmationPrompt && dlgres == DialogResult.Yes)
                {
                    if (!hasFileDetailForUpdate) LoadFileDetails(lstFileNames.SelectedIndex);
                    return ValidateNames(false);
                }
                else if (confirmationPrompt && dlgres == DialogResult.No)
                    isValid = false;

                isValid = false;
            }

            return isValid;
        }

        private void btnUpdateFiles_Click(object sender, EventArgs e)
        {
            if (!GetValidFileList(false).Any()) return;

            bool allowUpdateInDebug = false;
            if (Program.IsDebug)
                allowUpdateInDebug = MessageBoxUtil.Show(Program.IsDebug, ResourceBulkRenamer.Msg_AllowUpdateInDebug, ResourceBulkRenamer.App_Confirmation, MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes;
            if (Program.IsDebug && !allowUpdateInDebug)
                return;

            string batchStamp;
            int countTotal = 0;
            int countUpdate = 0;
            int countError = 0;

            LastBatchStamp_bup = LastBatchStamp = batchStamp = DateTime.Now.ToString(ResourceBulkRenamer.Format_LogBatchstamp);
            //if (!mnuRevertLastBatch.Enabled) mnuRevertLastBatch.Enabled = true; -- in mum version

            string batchDesc = "";
            ObservableCollection<FileDetails> filesToUpdate = GetValidFileList(false)
                .Where(fdet => fdet.NameOrg != fdet.NameSuggested || fdet.NameOrg != fdet.NameCustom).ToObservableCollection();

            foreach (FileDetails fileDetails in filesToUpdate)
            {
                if (ValidateNames(false, fileDetails))
                {
                    // Accept batch description
                    if (fileDetails == filesToUpdate.First())
                    {
                        // Start fetching batch description on a separate thread
                        ObservableCollection<string> allBatchDesc = FetchBatchDetails();

                        // Enter batch description
                        batchDesc = Interaction.InputBox(ResourceBulkRenamer.Msg_BatchDescription, ResourceBulkRenamer.App_BatchDescription, SelectedBasePath.GetDirectoryNameOnly()); // ResourceBulkRenamer.Default_BatchDescription

                        // Check for duplicate batch description
                        int iRecursionCount = 0;
                        while (batchDesc.IsBlank() || allBatchDesc._Contains(batchDesc))
                        {
                            if (++iRecursionCount > maxBatchDescUpdateCount)
                            {
                                MessageBoxUtil.Show(Program.IsDebug, ResourceBulkRenamer.Msg_BatchDescriptionLoopMax.FormatTextFromParams(maxBatchDescUpdateCount), ResourceBulkRenamer.App_Validation, MessageBoxButtons.OK, MessageBoxIcon.Error);
                                return;
                            }

                            batchDesc = Interaction.InputBox(batchDesc.IsBlank() ? ResourceBulkRenamer.Msg_BatchDescriptionNoBlank : ResourceBulkRenamer.Msg_BatchDescriptionDupe,
                                ResourceBulkRenamer.App_BatchDescription_Attempts.FormatTextFromParams(ResourceBulkRenamer.App_BatchDescription, iRecursionCount, maxBatchDescUpdateCount),
                                batchDesc.IsBlank() ? "" : batchDesc + "_" + DateAndTime.Now.ToString(ResourceBulkRenamer.Format_LogBatchstamp)); // ResourceBulkRenamer.Default_BatchDescription
                        }

                        // Abort, if batch description not filled
                        if (batchDesc.IsBlank())
                        {
                            MessageBoxUtil.Show(Program.IsDebug, ResourceBulkRenamer.Msg_BatchDescriptionBlankAbort, ResourceBulkRenamer.App_Validation, MessageBoxButtons.OK, MessageBoxIcon.Error);
                            return;
                        }
                    }

                    string src = fileDetails.NameOrg;
                    string trgt = fileDetails.TakeSuggested ? fileDetails.NameSuggested : fileDetails.NameCustom;
                    string fileExt = !fileDetails.FileExtn.IsNullOrEmpty() ? "." + fileDetails.FileExtn : "";

                    // Source file name
                    src = SelectedBasePath + Path.DirectorySeparatorChar + src.Trim() + fileExt.ToLower();
                    string trgt_full = SelectedBasePath + Path.DirectorySeparatorChar + trgt.Trim() + fileExt.ToLower();

                    if (File.Exists(src))
                    {
                        // Create new directory, if not exists
                        if (!Directory.Exists(Path.GetDirectoryName(trgt_full)))
                        {
                            if (allowUpdateInDebug)
                                MessageBoxUtil.Show(Program.IsDebug, "Debug mode : Creates new directory here..");
                            Directory.CreateDirectory(Path.GetDirectoryName(trgt_full));
                        }

                        string msg = "";
                        try
                        {
                            string finalUpdate = "";
                            string forammted = FormatAnnot(trgt);

                            // Updates annotations
                            if ((trgt.Contains("(") || trgt.Contains(")")) && forammted != trgt)
                                finalUpdate = Interaction.InputBox(ResourceBulkRenamer.Msg_AnnotationUpdate.FormatTextFromParams(trgt, forammted), ResourceBulkRenamer.App_AnnotationUpdate, forammted);
                            if (!finalUpdate.IsNullOrEmpty() && finalUpdate != trgt && finalUpdate.ToLower() == trgt.ToLower()) trgt = finalUpdate;

                            // Target file name
                            trgt = SelectedBasePath + Path.DirectorySeparatorChar + trgt.Trim() + fileExt.ToLower();

                            // Update and status
                            if (!Program.IsDebug || (Program.IsDebug && allowUpdateInDebug)) File.Move(src, trgt);
                            if (File.Exists(trgt)) { msg = sSuccessful; countUpdate++; }
                        }
                        catch (Exception ex) { msg = "Failure: " + ex.Message.TrimEnd(Utilities.cNewLine.ToCharArray()); countError++; }

                        // Update log
                        if (!allowUpdateInDebug) msg = sDebugMode;
                        if (!IgnoreLogPrompt) UpdateLog(src, trgt, fileExt, batchStamp, msg, batchDesc);
                        countTotal++;
                    }
                }
            }

            IgnoreLogPrompt = false;
            btnLoadFiles_Click(sender, e);
            MessageBoxUtil.Show(Program.IsDebug, countTotal > 0
                ? ResourceBulkRenamer.Msg_UpdateCount.FormatTextFromParams(countTotal, countUpdate, countError) // "Final counts:\t\t" + Utilities.cNewLine + "   Total: " + countTotal + Utilities.cNewLine + "   Updates: " + countUpdate + Utilities.cNewLine + "   Errors: " + countError
                : ResourceBulkRenamer.Msg_UpdateCount_NoFiles, ResourceBulkRenamer.App_UpdateCount, MessageBoxButtons.OK, MessageBoxIcon.Information);
        }

        private ObservableCollection<string> FetchBatchDetails()
        {
            DataTable dtLog = Utilities.ReadCSV(SelectedLogFile, tableName: "BatchDetails");
            if (dtLog.Rows.Count == 0) return new ObservableCollection<string>();

            DataRow[] drLogs = null;
            drLogs = dtLog.Select("Status IN ( '{0}', '{1}' )".FormatTextFromParams(sSuccessful, sDebugMode), "Batch_Desc ASC");

            return GetFileListFromDatatable(drLogs)._Select(log => log.Description).Distinct().ToObservableCollection();
        }

        /// <summary>
        /// Updates log
        /// </summary>
        /// <param name="src">Source file name</param>
        /// <param name="trgt">Target file name</param>
        /// <param name="fileExt">File extension</param>
        /// <param name="msg">Message to log</param>
        /// <param name="batchStamp">Batch stamp</param>
        /// <param name="batchDesc">Batch description</param>
        public void UpdateLog(string src, string trgt, string fileExt, string batchStamp, string msg, string batchDesc)
        {
            if (SelectedLogFile.IsNullOrEmpty() || File.GetAttributes(SelectedLogFile).HasFlag(FileAttributes.Directory))
            {
                if (MessageBoxUtil.Show(Program.IsDebug, ResourceBulkRenamer.Msg_LogNotSelected, ResourceBulkRenamer.App_LogNotSelected, MessageBoxButtons.YesNo) == DialogResult.Yes)
                    btnBrowseLogFile_Click(null, null);
                else
                { IgnoreLogPrompt = true; return; }
            }
            else
            {
                if (!Path.GetDirectoryName(SelectedLogFile).ValidatePath(ePathValidation.Directory))
                    Directory.CreateDirectory(Path.GetDirectoryName(SelectedLogFile));
                if (!SelectedLogFile.ValidatePath(ePathValidation.File))
                    Utilities.CreateFile(SelectedLogFile);

                if (File.Exists(SelectedLogFile))
                {
                    // Add header
                    //string header = "Batch" + Utilities.cDelim + "Time_stamp" + Utilities.cDelim + "Base_Dir" + Utilities.cDelim + "Old_Name" + Utilities.cDelim + "New_Name" + Utilities.cDelim + "Extn" + Utilities.cDelim + "Status" + Utilities.cDelim + "Batch_Desc" + Utilities.cNewLine;
                    //if (File.ReadAllText(SelectedLogFile).IsNullOrEmpty())
                    //    File.AppendAllText(SelectedLogFile, header, Utilities.DefaultEncoding);

                    // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
                    string header = "";
                    string logText = "";

                    char cKeyDelim = ':';
                    Dictionary<string, string> LogValues = new Dictionary<string, string>();
                    LogValues["01" + cKeyDelim + "Batch"] = batchStamp;
                    LogValues["02" + cKeyDelim + "Time_stamp"] = DateTime.Now.ToString(ResourceBulkRenamer.Format_LogRecTimestamp);
                    LogValues["03" + cKeyDelim + "Base_Dir"] = SelectedBasePath.MaskCSVChars();
                    LogValues["04" + cKeyDelim + "Old_Name"] = Path.GetFileNameWithoutExtension(src.MaskCSVChars());
                    LogValues["05" + cKeyDelim + "New_Name"] = Path.GetFileNameWithoutExtension(trgt.MaskCSVChars());
                    LogValues["06" + cKeyDelim + "Extn"] = fileExt.Trim('.');
                    LogValues["07" + cKeyDelim + "Status"] = msg.MaskCSVChars();
                    LogValues["08" + cKeyDelim + "Batch_Desc"] = batchDesc.MaskCSVChars();

                    foreach (string key in LogValues.Keys.OrderBy(ky => Convert.ToInt32(ky.Split(cKeyDelim).First())))
                    {
                        string currKey = key.Split(cKeyDelim).Last();
                        string currHeader = (!header.IsNullOrEmpty() ? Utilities.cDelim.ToString() : "") + currKey;

                        header += currHeader; // Create header
                        logText += currHeader.Replace(currKey, LogValues[key]); // Create log
                    }

                    // Add header
                    if (File.ReadAllText(SelectedLogFile).IsNullOrEmpty())
                        File.AppendAllText(SelectedLogFile, header + Utilities.cNewLine, Utilities.DefaultEncoding);
                    // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

                    // Create log
                    //if (batchDesc.IsNullOrEmpty()) batchDesc = ResourceBulkRenamer.Default_BatchDescription;
                    //string logText = batchStamp + Utilities.cDelim + DateTime.Now.ToString(ResourceBulkRenamer.Format_LogRecTimestamp) + Utilities.cDelim + SelectedBasePath + Utilities.cDelim + Path.GetFileNameWithoutExtension(src) + Utilities.cDelim + Path.GetFileNameWithoutExtension(trgt) + Utilities.cDelim + fileExt.Trim('.') + Utilities.cDelim + msg + Utilities.cDelim + batchDesc + Utilities.cNewLine;

                    // Format log and append
                    logText = logText.MaskCSVChars().Replace(Utilities.cDelim, Utilities.cCSVDelim);
                    File.AppendAllText(SelectedLogFile, logText + Utilities.cNewLine, Utilities.DefaultEncoding);
                }
            }





            /*
            // Example #1: Write an array of strings to a file. 
            // Create a string array that consists of three lines. 
            string[] lines = { "First line", "Second line", "Third line" };
            // WriteAllLines creates a file, writes a collection of strings to the file, 
            // and then closes the file.
            File.WriteAllLines(@"C:\Users\Public\TestFolder\WriteLines.txt", lines);


            // Example #2: Write one string to a text file. 
            string text = "A class is the most powerful data type in C#. Like a structure, " +
                           "a class defines the data and behavior of the data type. ";
            // WriteAllText creates a file, writes the specified string to the file, 
            // and then closes the file.
            File.WriteAllText(@"C:\Users\Public\TestFolder\WriteText.txt", text);

            // Example #3: Write only some strings in an array to a file. 
            // The using statement automatically closes the stream and calls  
            // IDisposable.Dispose on the stream object. 
            using (StreamWriter file = new StreamWriter(@"C:\Users\Public\TestFolder\WriteLines2.txt"))
            {
                foreach (string line in lines)
                {
                    // If the line doesn't contain the word 'Second', write the line to the file. 
                    if (!line.Contains("Second"))
                    {
                        file.WriteLine(line);
                    }
                }
            }

            // Example #4: Append new text to an existing file. 
            // The using statement automatically closes the stream and calls  
            // IDisposable.Dispose on the stream object. 
            using (StreamWriter file = new StreamWriter(@"C:\Users\Public\TestFolder\WriteLines2.txt", true))
            {
                file.WriteLine("Fourth line");
            }
            */
        }

        /// <summary>
        /// Evaluates a directory for Prev/Next/Up/Down functions
        /// </summary>
        /// <param name="fetchType">Fetch type</param>
        /// <param name="srcPath">Path to validate against</param>
        /// <param name="destPath">Path (updated, only when valid)
        /// <para>Note: this passed ByRef</para></param>
        /// <returns></returns>
        public bool EvaluateDirectory(eFetchTypes fetchType, string srcPath, ref string destPath)
        {
            if (!srcPath.ValidatePath(ePathValidation.Directory)) return false;

            ObservableCollection<string> allDirectories = new ObservableCollection<string>();
            int currDirectoryIndex = -1;
            bool pathValidations = false;
            bool result = false;

            if (!srcPath.Contains(Path.DirectorySeparatorChar) && !srcPath.IsNullOrEmpty())
                srcPath += Path.DirectorySeparatorChar;

            if (fetchType != eFetchTypes.All)
            {
                destPath = srcPath;
                pathValidations = srcPath.ValidatePath(ePathValidation.Directory) && srcPath != Path.DirectorySeparatorChar.ToString();

                if (pathValidations)
                {
                    if (fetchType == eFetchTypes.Prev || fetchType == eFetchTypes.Next)
                    {
                        if (!Directory.GetParent(srcPath).IsNull())
                            allDirectories = Utilities.GetValidDirectories(Directory.GetParent(srcPath).FullName).ToObservableCollection();
                        else
                            allDirectories = DriveInfo.GetDrives().Where(drv => drv.IsReady)
                                .Select(drv => drv.RootDirectory.Root.FullName).ToObservableCollection();

                        currDirectoryIndex = allDirectories.IndexOf(srcPath);
                    }
                }
            }

            string tmpPath = "";
            string sLocationInfo = "";
            CUtil_DirectionButtons btnFetchDir = null;
            ToolStripMenuItem mnuFetchDir = null;

            switch (fetchType)
            {
                case eFetchTypes.Prev:
                    btnFetchDir = btnFetchDirPrev;
                    mnuFetchDir = mnuFetchDirPrev;
                    break;

                case eFetchTypes.Next:
                    btnFetchDir = btnFetchDirNext;
                    mnuFetchDir = mnuFetchDirNext;
                    break;

                case eFetchTypes.Up:
                    btnFetchDir = btnFetchDirUp;
                    mnuFetchDir = mnuFetchDirUp;
                    break;

                case eFetchTypes.Down:
                    btnFetchDir = btnFetchDirDown;
                    mnuFetchDir = mnuFetchDirDown;
                    break;

                case eFetchTypes.All:
                    foreach (eFetchTypes ftype in Enum.GetValues(typeof(eFetchTypes)).Cast<eFetchTypes>())
                        if (ftype != fetchType) EvaluateDirectory(ftype, SelectedBasePath, ref destPath);
                    break;

                default:
                    result = false;
                    break;
            }

            if (fetchType != eFetchTypes.All)
            {
                tmpPath = srcPath.GetDirectoryByDirection(fetchType, out result, out sLocationInfo, allDirectories);
                if (fetchType == eFetchTypes.Prev || fetchType == eFetchTypes.Next) lblSubDirectoryCount.Text = sLocationInfo;
                mnuFetchDir.Enabled = btnFetchDir.Enabled = result = (pathValidations && result);
                if (result) destPath = tmpPath;
            }

            return result;
        }

        /// <summary>
        /// Formats annotations/captions
        /// </summary>
        /// <param name="value">Value to operate one</param>
        /// <returns>Formatted string</returns>
        private string FormatAnnot(string value)
        {
            ObservableCollection<char> lstChar = value.ToCharArray().ToObservableCollection();

            bool isStart = false;
            bool isStop = true;
            string value_final = "";

            foreach (char chr in lstChar)
            {
                if (chr == '(')
                { isStart = true; isStop = false; value_final += chr; }
                else if (chr == ')')
                { isStart = false; isStop = true; value_final += chr; }
                else
                { value_final += (isStart & !isStop) ? chr.ToString().ToLower() : chr.ToString(); }
            }

            return value_final;
        }

        private void numTrim_ValueChanged(object sender, EventArgs e)
        {
            if (!SupressSuggestion)
            {
                NumericUpDown num = (NumericUpDown)sender;
                if (!num.Enabled) return;

                DefineRange(num);

                dictTrimChange[KEY_LTrimChange] = Convert.ToInt32(numLTrim.Value) - custLTrimLength;
                dictTrimChange[KEY_RTrimChange] = Convert.ToInt32(numRTrim.Value) - custRTrimLength;

                //if (custLTrimLength > 0 && custRTrimLength > 0)
                //{
                //    if (num.Name == numLTrim.Name)
                //    {
                //        if (numLTrim.Value > 0)
                //            numLTrim.Value = (numLTrim.Value > numRTrim.Value - 1) ? numRTrim.Value - 1 : numLTrim.Value;
                //    }
                //    else if (num.Name == numRTrim.Name)
                //    {
                //        if (custRTrimLength > 0)
                //            numRTrim.Value = (numRTrim.Value < numLTrim.Value + 1) ? numLTrim.Value + 1 : numRTrim.Value;
                //    }
                //}

                custLTrimLength = Convert.ToInt32(numLTrim.Value);
                custRTrimLength = Convert.ToInt32(numRTrim.Value);

                /*if (SuggestCount > 0)*/
                btnSuggestNames_Click(sender, e);
            }
        }

        private void txtLogPath_TextChanged(object sender, EventArgs e)
        {
            SelectedLogFile = txtLogPath.Text;
            string tooltipText = SelectedLogFile.ValidatePath(ePathValidation.File) ? Path.GetFileName(SelectedLogFile.Trim()) : ResourceBulkRenamer.Default_InvalidPathTooltip;
            tltpMain.SetToolTip(txtLogPath, "Selected log: " + Utilities.cNewLine + "   " + tooltipText);
        }

        private void txtBasePath_TextChanged(object sender, EventArgs e)
        {
            ClearEditor();

            SelectedBasePath = IsUsingComboText ? txtBasePath.Text : txtBasePath_AutoSugg.Text;
            string tooltipText = SelectedBasePath.ValidatePath(ePathValidation.Directory) ? SelectedBasePath.Trim() : ResourceBulkRenamer.Default_InvalidPathTooltip;
            if (IsUsingComboText) tltpMain.SetToolTip(txtBasePath, "Selected base path: " + Utilities.cNewLine + "   " + tooltipText);
            else tltpMain.SetToolTip(txtBasePath_AutoSugg, "Selected base path: " + Utilities.cNewLine + "   " + tooltipText);

            #region Path suggestion
            if (IsUsingComboText)
            {
                PopulatePathSuggestion(cmbPathSuggestion, txtBasePath.Text);
                if (!IsNavigating) cmbPathSuggestion.DroppedDown = true;
            }
            else if (!IsUsingComboText)
                PopulatePathSuggestion(txtBasePath_AutoSugg, txtBasePath_AutoSugg.Text);
            #endregion

            #region File load
            string tmpPath = "";
            btnLoadFiles.Enabled = SelectedBasePath.ValidatePath(ePathValidation.Directory);
            EvaluateDirectory(eFetchTypes.All, SelectedBasePath, ref tmpPath);
            if (chkAutoLoad.Checked) btnLoadFiles_Click(sender, e);
            #endregion
        }

        private void txtBasePath_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (Form.ModifierKeys == Keys.Control && e.KeyChar == ' ')
            { e.Handled = true; cmbPathSuggestion.DroppedDown = true; }
        }

        private void txtBasePath_KeyDown(object sender, KeyEventArgs e)
        {
            if (Form.ModifierKeys == Keys.Control)
            {
                if (e.KeyCode == Keys.Down)
                { cmbPathSuggestion.DroppedDown = true; cmbPathSuggestion.Focus(); }
                if (e.KeyCode == Keys.Escape)
                { cmbPathSuggestion.DroppedDown = false; txtBasePath.Focus(); }
            }
        }

        /// <summary>
        /// Updates the range max/min values of NumericUpDown for trimming
        /// </summary>
        /// <param name="num">NumericUpDown to be updated for trimming
        /// <para>(pass &apos;null&apos; to update both NumericUpDown for trimming)</para></param>
        private void DefineRange(NumericUpDown num)
        {
            if (num.IsNull()) // (numLTrim.Value == 0 && numRTrim.Value == 0) || IsNull(num)
            {
                numLTrim.Minimum = numRTrim.Minimum = 0;
                numLTrim.Maximum = numRTrim.Maximum = MaxTrimRange;

                //numLTrim.Maximum = MaxTrimRange / 2 + (MaxTrimRange % 2 == 1 ? 1 : 0);
                //numRTrim.Maximum = MaxTrimRange / 2;
            }
            else
            {
                //if (num.Name == numLTrim.Name)
                //    numRTrim.Maximum = 0;
                //else if (num.Name == numRTrim.Name)
                //    numLTrim.Maximum = 0;
            }
        }

        private void btnDelete_Click(object sender, EventArgs e)
        {
            ObservableCollection<FileDetails> validFiles = GetValidFileList(false);

            if (validFiles.HasCount(0))
            { MessageBoxUtil.Show(Program.IsDebug, "Validation: No more files to delete!\t", "No files.."); return; }

            if (lstFileNames.SelectedItems.Count == 0) return;

            int selectedFileIndex = -1;
            int minId = validFiles.Select(fdet => fdet.FileId).Min();

            DateTime timeStamp = DateTime.Now;
            if (lstFileNames.SelectedItems.Count == 1) // Performance improvement only, ELSE block is generic
                DeleteFile((FileDetails)lstFileNames.SelectedItems[0], timeStamp, true); // Delete item & update list
            else
                // RKD : removed foreach
                //for each (FileDetails fdet in lstFileNames.SelectedItems)
                //    DeleteFile(fdet, timeStamp, false); // Delete item & update list
                lstFileNames.SelectedItems.Cast<FileDetails>().ForEach(fdet => DeleteFile(fdet, timeStamp, false));

            // Refresh file list
            validFiles = GetValidFileList(false);

            // Clear editor if deleted item is in editor
            if (!txtId.Text.IsNullOrEmpty())
                if (!validFiles.Where(fdet => fdet.FileId == Convert.ToInt32(txtId.Text)).Any())
                    ClearEditor();

            if (validFiles.Any())
            {
                // Set selection
                int minDelId = AllDeletedIds.Where(del => del.TimeStamp == timeStamp).Select(del => del.FileDetails.FileId).Min();
                selectedFileIndex = (minDelId <= minId)
                    ? validFiles.Select(fdet => fdet.FileId).Min()
                    : validFiles.Where(fdet => fdet.FileId < minDelId).Select(fdet => fdet.FileId).Max();
                lstFileNames.SelectedItems.Clear();
                lstFileNames.DataSource = validFiles;
                lstFileNames.SelectedItem = validFiles.Where(fdet => fdet.FileId == selectedFileIndex).FirstOrDefault();
                selectedFileIndex = lstFileNames.SelectedIndex;

                // Refresh and load
                if (validFiles.Where(fdet => fdet.FileId == (!txtId.Text.IsNullOrEmpty() ? Convert.ToInt32(txtId.Text) : 0)).HasCount(0))
                    LoadFileDetails(selectedFileIndex);

                mnuSuggest.Enabled = grpOutput.Enabled = (validFiles.Count() > 1);
            }
            else
                btnUpdateFiles.Enabled = false; // No need to clear editor; handled while deleting file with ClearAll

            // REQ-##123 : Update LTrim max value on delete of file
            UpdateMaxTrimRange(); DefineRange(null);
        }

        private void ReloadList(bool refreshFilters, bool restoreFilters = false)
        {
            if (refreshFilters) RefreshFilters(restoreFilters);

            IsReloading = true;
            ObservableCollection<FileDetails> allFileDetails = GetValidFileList(false).ToObservableCollection();
            lstFileNames.DataSource = allFileDetails;
            RefreshCount(); UpdateMaxTrimRange();
            IsReloading = false;

            if (refreshFilters && allFileDetails.Any()) LoadFileDetails(0);
        }

        private void RefreshCount()
        {
            txtRecCount.Text = GetValidFileList(false).Count() > 0 ? GetValidFileList(false).Count().ToString() : "--";
            txtSelCount.Text = lstFileNames.SelectedItems.Count > 0 ? lstFileNames.SelectedItems.Count.ToString() : "--";
        }

        private void RefreshFilters(bool restoreFilters)
        {
            // Backup selection
            ObservableCollection<ExtnFilters> selectedFilters = null;
            if (restoreFilters) selectedFilters = AllFilters.Where(fltr => (bool)fltr.Value == true).ToObservableCollection();

            ObservableCollection<CheckBox> allCheckBoxes = new ObservableCollection<CheckBox>();
            AllFilters = GetValidFileList(true).Select(fltr => fltr.FileExtn).Distinct().OrderBy(fltr => fltr)
                .Select(fltr => new ExtnFilters(fltr)).ToObservableCollection();

            // Restore selection
            //if (restoreFilters) // RKD
            //    AllFilters._ForEach(fltr => fltr.Value = (selectedFilters.Select(sel => sel.Id).Contains(fltr.Id) ? selectedFilters.Where(sel => sel.Id == fltr.Id).First().Value : false));
            if (restoreFilters)
            {
                foreach (ExtnFilters ext in AllFilters)
                {
                    ObservableCollection<ExtnFilters> tmp = selectedFilters.Where(sel => Convert.ToString(sel.Id) == Convert.ToString(ext.Id)).ToObservableCollection();
                    if (tmp.Any()) ext.Value = tmp.First().Value;
                    else ext.Value = false;
                }
            }

            cmbExtnFilters.DataSource = AllFilters.ToObservableCollection();
            cmbExtnFilters.ValueMember = "Value";
            cmbExtnFilters.DisplayMember = "Id";
            cmbExtnFilters.CheckBoxItems.CheckBoxCheckedChanged += new EventHandler(chkExtnFilterSelMode_CheckedChanged);

            txtExtnFilters.Text = GenerateFilterCSV();
        }

        private void chkExtnFilterSelMode_CheckedChanged(object sender, EventArgs e)
        {
            UpdateFilterExtn();
        }

        private void UpdateFilterExtn()
        {
            //foreach (CheckBox chkExtnFilter in cmbExtnFilters.Items)
            //{
            //    ExtnFilters extnFilter = AllFilters.Where(fltr => Convert.ToString(fltr.Id) == chkExtnFilter.Name.Replace(sCheckBoxNamePrefix, "")).First();
            //    extnFilter.Value = chkExtnFilter.Checked;
            //}

            EvaluateCheckedState();
            RefreshExtnFilterTooltip();
        }

        private void EvaluateCheckedState()
        {
            if (AllFilters.Select(fltr => (bool)fltr.Value).Distinct().HasCount(1, eComparers.GreaterThan))
            {
                chkExtnFilterSelMode.ThreeState = true;
                chkExtnFilterSelMode.CheckState = CheckState.Indeterminate;
            }
            else if (AllFilters.Select(fltr => (bool)fltr.Value).Distinct().HasCount(1))
                chkExtnFilterSelMode.CheckState = (AllFilters.Select(fltr => (bool)fltr.Value).Distinct().First() ? CheckState.Checked : CheckState.Unchecked);
        }

        private void RefreshExtnFilterTooltip()
        {
            string tooltipText = GenerateFilterCSV();
            if (txtExtnFilters.Text != tooltipText)
            {
                txtExtnFilters.Text = tooltipText;
                tltpMain.SetToolTip(txtExtnFilters, tooltipText);
                tltpMain.SetToolTip(cmbExtnFilters, tooltipText);

                ReloadList(false);
            }
        }

        private string GenerateFilterCSV()
        {
            string tooltipText = "";
            foreach (ExtnFilters extnFilters in AllFilters)
            {
                if ((bool)extnFilters.Value)
                    tooltipText += (tooltipText.IsNullOrEmpty() ? "" : ", ") + extnFilters.Id;
            }

            return tooltipText;
        }

        private ObservableCollection<FileDetails> GetValidFileList(bool ignoreExtnFilters) // fetchUnfiltered
        {
            if (!chkAnyExtn.Checked || ignoreExtnFilters)
                return AllFileDetails.Where(fdet => !AllDeletedIds.Select(del => del.FileDetails.FileId).Contains(fdet.FileId))
                    .OrderBy(fdet => fdet.FileId).ToObservableCollection();
            else
            {
                return AllFileDetails.Where(fdet => !AllDeletedIds.Select(del => del.FileDetails.FileId).Contains(fdet.FileId)
                        && AllFilters.Where(fltr => (bool)fltr.Value).Select(fltr => fltr.Id).Contains(fdet.FileExtn))
                    .OrderBy(fdet => fdet.FileId).ToObservableCollection();
            }
        }

        private void DeleteFile(FileDetails fileDetails, DateTime timeStamp, bool reloadList)
        {
            if (!fileDetails.IsNull())
            {
                AllDeletedIds.Add(new FileDeletionDetails { FileDetails = fileDetails, TimeStamp = timeStamp });

                ObservableCollection<FileDetails> validFiles = GetValidFileList(false);
                cmnuDeleteSelection.Enabled = btnDelete.Enabled = btnExport.Enabled = (validFiles.Any());
                if (!validFiles.Any()) ClearAll();

                if (reloadList) ReloadList(true, true);
            }
        }

        private void DeleteFile(int fileId, DateTime timeStamp, bool reloadList)
        {
            FileDetails fileDetails = AllFileDetails.Where(fdet => fdet.FileId == fileId).First();
            DeleteFile(fileDetails, timeStamp, reloadList);
        }

        private void DeleteFile(IEnumerable<int> fileIds, DateTime timeStamp, bool reloadList)
        {
            IEnumerator<int> item = fileIds.GetEnumerator();
            item.Reset();

            while (item.MoveNext()) { DeleteFile(item.Current, timeStamp, reloadList); }
        }

        private void btnFetchDirectory_Click(object sender, EventArgs e)
        {
            eFetchTypes fetchType = Utilities.GetEnumFromControl<eFetchTypes>(sender, "btnFetchDir", "mnuFetchDir");
            FetchDirectory(fetchType, sender, e);
            if (IsUsingComboText) txtBasePath.Focus(); else txtBasePath_AutoSugg.Focus();
        }

        /// <summary>
        /// Fetches directory based on specified direction
        /// </summary>
        /// <param name="fetchType">Fetch type</param>
        /// <param name="sender">Sender</param>
        /// <param name="e">EventArgs</param>
        private void FetchDirectory(eFetchTypes fetchType, object sender, EventArgs e)
        {
            string tmpSelectedBasePath = SelectedBasePath;
            if (EvaluateDirectory(fetchType, SelectedBasePath, ref tmpSelectedBasePath))
            {
                IsNavigating = true; if (IsUsingComboText) txtBasePath.Text = tmpSelectedBasePath; else txtBasePath_AutoSugg.Text = tmpSelectedBasePath; IsNavigating = false;
                if (chkAutoLoad.Checked) btnLoadFiles_Click(sender, e);
            }
        }

        private void btnExport_Click(object sender, EventArgs e)
        {
            #region Form :: Export extn. selector
            frmExportExtnChooser = new Form
            {
                Text = "Export extn. selector",
                StartPosition = FormStartPosition.CenterParent,
                ControlBox = false,
                MinimizeBox = false,
                MaximizeBox = false,
                ShowIcon = false,
                ShowInTaskbar = false
            };

            GroupBox grpSelector = new GroupBox
            {
                Name = "grpSelector",
                Text = " .: Select extn. :. ",
                AutoSize = false,
                Width = 150,
                Location = new Point(10, 10)
            };

            foreach (IdNameValueMember extn in AllExportExtn)
            {
                RadioButton rad = new RadioButton
                {
                    Name = "rad" + extn.Name,
                    Text = (string)extn.Name,
                    Checked = (extn.Name == AllExportExtn.First().Name)
                };

                rad.Location = new Point(20, rad.Height * (int)extn.Id);
                grpSelector.Controls.Add(rad);
            }

            CUtil_Button btnOK = new CUtil_Button
            {
                Name = "btnOK",
                Text = "&OK",
                Width = 80
                //,
                //Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)))
            };

            btnOK.Click += new EventHandler(btnOK_Click);

            frmExportExtnChooser.AcceptButton = btnOK;
            frmExportExtnChooser.Controls.Add(btnOK);
            frmExportExtnChooser.Controls.Add(grpSelector);

            frmExportExtnChooser.Width = grpSelector.Width + 30;
            btnOK.Location = new Point((frmExportExtnChooser.Width - btnOK.Width) / 2, 20 + grpSelector.Height);
            frmExportExtnChooser.Height = btnOK.Top + btnOK.Height + 40;
            frmExportExtnChooser.MinimumSize = frmExportExtnChooser.MaximumSize = frmExportExtnChooser.Size;
            #endregion

            frmExportExtnChooser.ShowDialog(this);
        }

        private void btnOK_Click(object sender, EventArgs e)
        {
            SelectedExportExtn = "";

            foreach (Control ctrl in frmExportExtnChooser.Controls["grpSelector"].Controls)
            {
                if (ctrl.GetType() == typeof(RadioButton))
                {
                    RadioButton rad = (RadioButton)ctrl;
                    if (rad.Checked) { SelectedExportExtn = rad.Text; break; }
                }
            }

            if (SelectedExportExtn.IsNullOrEmpty()) MessageBoxUtil.Show(Program.IsDebug, "Please select an extn.");
            else
            {
                frmExportExtnChooser.Close();

                OpenFileDialog dlgSaveFile = new OpenFileDialog
                {
                    Title = "Select file to export..",
                    AddExtension = true,
                    CheckFileExists = false,
                    CheckPathExists = false,
                    DefaultExt = SelectedExportExtn,
                    RestoreDirectory = true
                };

                IdNameValueMember selectedExportExtn = AllExportExtn.Where(extn => Convert.ToString(extn.Name) == SelectedExportExtn).First();
                dlgSaveFile.Filter = FormatDialogFilters(selectedExportExtn).TrimEnd('|');
                dlgSaveFile.InitialDirectory = !txtImportPath.Text.IsPathFormatValid() ? sDefaultPath
                    : Directory.Exists(Path.GetDirectoryName(txtImportPath.Text)) ? txtImportPath.Text : sDefaultPath;
                dlgSaveFile.FileName = File.Exists(SelectedExportFile) ? SelectedExportFile : "Export_" + DateTime.Now.ToString("yyyyMMdd_HHmmss");

                if (dlgSaveFile.ShowDialog() == DialogResult.OK)
                {
                    txtImportPath.Text = dlgSaveFile.FileName;
                    ExportFileDetails();
                }
            }
        }

        private void ExportFileDetails()
        {
            if (SelectedExportFile.IsNullOrEmpty() || Path.GetExtension(SelectedExportFile).IsNullOrEmpty())
            {
                if (MessageBoxUtil.Show(Program.IsDebug, "Export file not selected." + Utilities.cNewLine + "Select now?", "Log file..", MessageBoxButtons.YesNo) == DialogResult.Yes)
                    btnBrowseAsynchFile_Click(null, null);
                else
                    return;
            }
            else
            {
                if (!Directory.Exists(Path.GetDirectoryName(SelectedExportFile))) Directory.CreateDirectory(Path.GetDirectoryName(SelectedExportFile));
                if (!File.Exists(SelectedExportFile))
                {
                    FileStream fs = File.Open(SelectedExportFile, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None);
                    fs.Flush();
                    fs.Close();
                    fs.Dispose();
                }

                if (File.Exists(SelectedExportFile))
                {
                    if (SelectedExportExtn == eExportFileExtn.CSV.ToString().ToLower())
                    {
                        string header = "|Id|Full_Path|Nam_Org|Name_Sugg|Name_Cust|File_Extn|";
                        //FileStream fs = File.Open(SelectedExportFile, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);

                        //TextReader tr = new StreamReader(SelectedExportFile);
                        //tr.

                        string rec = ""; string curr = "";
                        foreach (FileDetails fdet in GetValidFileList(false))
                        {
                            curr = header.Replace("|Id|", "|" + fdet.FileId + "|")
                                            .Replace("|Full_Path|", "|" + fdet.FullPath + "|")
                                                .Replace("|Nam_Org|", "|" + fdet.NameOrg + "|")
                                                    .Replace("|Name_Sugg|", "|" + fdet.NameSuggested + "|")
                                                        .Replace("|Name_Cust|", "|" + fdet.NameCustom + "|")
                                                            .Replace("|File_Extn|", "|" + fdet.FileExtn + "|");
                            rec += curr.Trim('|').Replace('|', ',') + Utilities.cNewLine;
                        }
                        rec = rec.Trim(',') + Utilities.cNewLine;

                        File.AppendAllText(SelectedExportFile, header.Replace('|', ',').Trim(',') + Utilities.cNewLine, Utilities.DefaultEncoding); // header
                        File.AppendAllText(SelectedExportFile, rec, Utilities.DefaultEncoding); // body
                    }
                    else if (SelectedExportExtn == eExportFileExtn.XLS.ToString().ToLower())
                    {

                    }
                    else if (SelectedExportExtn == eExportFileExtn.TXT.ToString().ToLower())
                    {

                    }
                    else
                    {

                    }
                }
            }
        }

        private void btnImport_Click(object sender, EventArgs e)
        {

        }

        private void btnBrowseAsynchFile_Click(object sender, EventArgs e)
        {

        }

        private void cmbExtnSelector_SelectedIndexChanged(object sender, EventArgs e)
        {
            IdNameValueMember idval = (IdNameValueMember)cmbExtnSelector.SelectedItem;
            if (!idval.Description.IsNullOrEmpty() && !SelectedExtn.IsNullOrEmpty())
            {
                tltpMain.SetToolTip(cmbExtnSelector, idval.Description);
                tltpMain.SetToolTip(btnLoadExtn, chkAnyExtn.Checked
                    ? ResourceBulkRenamer.ToolTip_Extn_All.FormatTextFromParams(SelectedExtn.ToUpper())
                    : ResourceBulkRenamer.ToolTip_Extn_Loaded.FormatTextFromParams(SelectedExtn.ToUpper(), cmbExtnSelector.Text.ToUpper(), idval.Description));
            }
        }

        private void cmbExtnSelector_SelectedValueChanged(object sender, EventArgs e)
        {
            // RKD
            //if (AllFileExtenstions.IsNull() && !cmbExtnSelector.Text.IsNullOrEmpty())
            //    if (cmbExtnSelector.SelectedIndex != -1 && AllFileExtenstions.Select(idval => idval.Name).Contains(cmbExtnSelector.SelectedValue))
            //        MessageBoxUtil.Show(cmbExtnSelector.SelectedValue + Utilities.cNewLine + cmbExtnSelector.Text);
        }

        private void ClickToClear(object sender, EventArgs e)
        {
            if (Form.ModifierKeys == Keys.Control)
            {
                Control txtPath = this.Controls["txt" + Utilities.GetControlName(sender).RemoveText("lbl")];
                txtPath.Text = "";
            }
        }

        private void txtImportPath_TextChanged(object sender, EventArgs e)
        {
            SelectedExportFile = txtImportPath.Text;
            string tooltipText = SelectedExportFile.ValidatePath(ePathValidation.File) ? Path.GetFileName(SelectedExportFile) : ResourceBulkRenamer.Default_InvalidPathTooltip;

            tltpMain.SetToolTip(txtImportPath, "Selected export file: " + Utilities.cNewLine + "   " + tooltipText);

            btnImport.Enabled = SelectedExportFile.ValidatePath(ePathValidation.File);
        }

        private void mnuClearAll_Click(object sender, EventArgs e)
        {
            mnuSelectAll_Click(sender, e);
            btnDelete_Click(sender, e);
        }

        private void mnuSelectAll_Click(object sender, EventArgs e)
        {
            // RKD : removed foreach
            //for each (FileDetails fdet in AllFileDetails)
            //    lstFileNames.SelectedItems.Add(fdet);
            AllFileDetails.ForEach(fdet => lstFileNames.SelectedItems.Add(fdet));
        }

        enum eUndoType
        {
            All,
            Last,
            Batch
        }

        private void mnuUndoDeletion_Click(object sender, EventArgs e)
        {
            eUndoType undoType = Utilities.GetEnumFromControl<eUndoType>(sender, "mnuUndo", "cmnuUndo");
            switch (undoType)
            {
                case eUndoType.All:
                    AllDeletedIds = new ObservableCollection<FileDeletionDetails>();
                    break;
                case eUndoType.Last:
                    if (AllDeletedIds.Any()) AllDeletedIds.Remove(AllDeletedIds.Last());
                    break;
                case eUndoType.Batch:
                    if (AllDeletedIds.Any())
                    {
                        List<int> delIds = AllDeletedIds.Where(del => del.TimeStamp == AllDeletedIds.Last().TimeStamp).Select(del => del.FileDetails.FileId).ToList();
                        // RKD : removed foreach
                        //foreach (int id in delIds) { AllDeletedIds.Remove(AllDeletedIds.Where(del => del.FileDetails.FileId == id).First()); }
                        delIds.ForEach(id => AllDeletedIds.Remove(AllDeletedIds.Where(del => del.FileDetails.FileId == id).First()));
                    }
                    break;
                default:
                    break;
            }

            ReloadList(true);
            //if (AllDeletedIds.Any()) lstFileNames.SelectedIndex = 0; // Don't change selection
        }

        private void chkAutoLoad_CheckedChanged(object sender, EventArgs e)
        {
            if (chkAutoLoad.Checked) btnLoadFiles_Click(sender, e);
        }

        private void mnuAboutApp_Click(object sender, EventArgs e)
        {
            new frmAbout().ShowDialog(this);
        }

        private void chkExtnFilterSelMode_CheckStateChanged(object sender, EventArgs e)
        {
            switch (chkExtnFilterSelMode.CheckState)
            {
                case CheckState.Checked:
                    chkExtnFilterSelMode.Text = "All";
                    break;
                case CheckState.Indeterminate:
                    chkExtnFilterSelMode.Text = "Some";
                    break;
                case CheckState.Unchecked:
                default:
                    chkExtnFilterSelMode.Text = "None";
                    break;
            }
        }

        private void lnkBasePath_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            if (SelectedBasePath.ValidatePath(ePathValidation.Directory))
                Utilities.OpenPath(SelectedBasePath);
        }

        private void PopulatePathSuggestion(ComboBox cmb, string searchText)
        {
            ObservableCollection<IdNameValueMember> allPathSuggestions = FetchPathSuggestion(searchText).ToObservableCollection();
            AllPathSuggestions = allPathSuggestions;

            cmb.DataSource = AllPathSuggestions;
            cmb.DisplayMember = cmb.ValueMember = "Id";
            cmb.SelectedIndex = -1;
        }

        /// <summary>
        /// Populates the AutoCompleteSource of specified textbox with suggested path
        /// </summary>
        /// <param name="txt">Specified TextBox</param>
        /// <param name="searchText">Search text</param>
        /// <exception cref="T:AccessViolationException">Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
        /// <para><see cref="http://stackoverflow.com/questions/3018029/autocomplete-texbox-error-write-to-protected-memory"/></para>
        /// <para>It is a bug in Windows Forms's wrapper of autocomplete APIs. Windows Forms does not protect the AutoCompleteCustomSource object
        /// from being replaced while it is being enumerated by a background thread created by autocomplete.</para></exception>
        private void PopulatePathSuggestion(TextBox txt, string searchText)
        {
            ObservableCollection<IdNameValueMember> allPathSuggestions = FetchPathSuggestion(searchText).ToObservableCollection();
            AllPathSuggestions = allPathSuggestions;

            lock (this)
            {
                txt.AutoCompleteCustomSource.Clear();
                if (AllPathSuggestions.Any())
                {
                    AutoCompleteStringCollection source = new AutoCompleteStringCollection();
                    source.AddRange(AllPathSuggestions.Select(dir => Convert.ToString(dir.Id)).ToArray());

                    txt.SuspendLayout();
                    txt.AutoCompleteCustomSource = source;
                    txt.ResumeLayout();
                }
            }
        }

        private ObservableCollection<IdNameValueMember> FetchPathSuggestion(string path)
        {
            if (path.IsNullOrEmpty())
                return new ObservableCollection<IdNameValueMember>();

            ObservableCollection<string> allPath = new ObservableCollection<string>();
            if (path.ToCharArray().Last() == Path.DirectorySeparatorChar && path.ValidatePath(ePathValidation.Directory))
                allPath = Utilities.GetValidDirectories(path.TrimEnd(Path.DirectorySeparatorChar)).ToObservableCollection();
            else
            {
                if (path.Contains(Path.DirectorySeparatorChar) && path.IsPathFormatValid())
                {
                    string parent = path.TrimAndTake(path.LastIndexOf(Path.DirectorySeparatorChar), eTrimDirection.Left);
                    string searchText = path.GetDirectoryNameOnly().ToLower();

                    if (parent.ValidatePath(ePathValidation.Directory))
                    {
                        allPath = Utilities.GetValidDirectories(parent + Path.DirectorySeparatorChar)
                            .Where(dir => dir.ValidatePath(ePathValidation.Directory)).ToObservableCollection();

                        if (!searchText.IsNullOrEmpty())
                        {
                            if (!searchText.Contains(chrWildcard.ToString())) // No wildcard-char
                                allPath = allPath.Where(dir => Path.GetFileName(dir).ToLower().StartsWith(searchText.Trim(chrWildcard))).ToObservableCollection();
                            else if (searchText.StartsWith(chrWildcard.ToString()) && searchText.EndsWith(chrWildcard.ToString())) // Starts/Ends with wildcard-char; Middle part specified
                                allPath = allPath.Where(dir => Path.GetFileName(dir).ToLower().Contains(searchText.Trim(chrWildcard))).ToObservableCollection();
                            else if (searchText.StartsWith(chrWildcard.ToString())) // Starts with wildcard-char; Last part specified
                                allPath = allPath.Where(dir => Path.GetFileName(dir).ToLower().EndsWith(searchText.Trim(chrWildcard))).ToObservableCollection();
                            else if (searchText.EndsWith(chrWildcard.ToString())) // Ends with wildcard-char; First part specified
                                allPath = allPath.Where(dir => Path.GetFileName(dir).ToLower().StartsWith(searchText.Trim(chrWildcard))).ToObservableCollection();
                        }
                    }
                }
            }

            return allPath.Select(dir => new IdNameValueMember { Id = dir }).ToObservableCollection();
        }

        private void mnuRefresh_Click(object sender, EventArgs e)
        {
            string msg = ResourceBulkRenamer.Msg_RestartApp.FormatTextFromParams((Debugger.IsAttached ? ResourceBulkRenamer.Msg_RestartApp_DebuggerStop : ""));
            switch (MessageBoxUtil.Show(null, msg, ResourceBulkRenamer.App_RestartApp, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question))
            {
                case DialogResult.No:
                    Application.Exit();
                    break;
                case DialogResult.Yes:
                    //InitializationControls(); 
                    Application.Restart();
                    break;
                case DialogResult.Cancel:
                default:
                    break;
            }
        }

        private void cmbPathSuggestion_SelectedIndexChanged(object sender, EventArgs e)
        {
            // RKD: USING TextBox suggestion instead of ComboBox suggestion
            if (Form.ModifierKeys == Keys.Control)
            {
                if (!cmbPathSuggestion.SelectedItem.IsNull() && !cmbPathSuggestion.ValueMember.IsNullOrEmpty())
                {
                    MessageBox.Show("cmbPathSuggestion_SelectedIndexChanged");
                    object obj = cmbPathSuggestion.SelectedItem;
                    txtBasePath.Text = Convert.ToString(obj.GetType().GetProperty(cmbPathSuggestion.ValueMember).GetValue(obj, null));
                }
            }
        }

        private void cmbPathSuggestion_KeyDown(object sender, KeyEventArgs e)
        {
            if (Form.ModifierKeys == Keys.Control)
            {
                if (e.KeyCode == Keys.Up || e.KeyCode == Keys.Escape)
                { cmbPathSuggestion.DroppedDown = false; txtBasePath.Focus(); SendKeys.Send("{End}"); }
            }
        }

        private void cmbPathSuggestion_KeyPress(object sender, KeyPressEventArgs e)
        {

        }

        private void chkMid_CheckStateChanged(object sender, EventArgs e)
        {
            if (isMidClicked) return;

            isMidClicked = true;
            string name = Utilities.GetControlName(sender);

            if (name == chkMid.Name) mnuMid.Checked = chkMid.Checked;
            else if (name == mnuMid.Name) chkMid.Checked = mnuMid.Checked;
            isMidClicked = false;
        }

        private void mnuOpenAppBase_Click(object sender, EventArgs e)
        {
            Utilities.OpenPath(Application.StartupPath, eOpeningApp.Default);
        }

        private void mnuUsersGuide_Click(object sender, EventArgs e)
        {
            if ((Application.StartupPath + Path.DirectorySeparatorChar + Program.BulkRenamerSettings["UserGuide"]).ValidatePath(ePathValidation.File))
                Utilities.OpenPath(Application.StartupPath + Path.DirectorySeparatorChar + Program.BulkRenamerSettings["UserGuide"], eOpeningApp.MSWord);
        }

        //keep_methods,_define_chkOpt.chkChanged_and_call_methods_based_on_value
        private void radCustom_CheckedChanged(object sender, EventArgs e)
        {
            if (!IsReloading)
            {
                if (AllowAutoCopy_Prompt && radCustom.Checked && txtNameCustom.Text != txtNameSugg.Text && !txtNameCustom.Text.IsNullOrEmpty())
                {
                    switch (MessageBoxUtil.Show(Program.IsDebug, "Copy from Suggested name?", "Confirmation..", MessageBoxButtons.YesNo, MessageBoxIcon.Question))
                    {
                        case DialogResult.Yes:
                            txtNameCustom.Text = txtNameSugg.Text;
                            if (chkAutoUpdate.Checked) btnUpdateDetails_Click(sender, e);
                            break;
                        case DialogResult.No:
                        default:
                            break;
                    }
                }
            }

            EvaluateManualFileDetailsUpdate();
        }

        private void radSuggested_CheckedChanged(object sender, EventArgs e)
        {
            if ((chkAutoUpdate.Checked && radSuggested.Checked) || txtNameCustom.Text == txtNameSugg.Text) btnUpdateDetails_Click(sender, e);

            EvaluateManualFileDetailsUpdate();
        }

        private void lstFileNames_SelectedIndexChanged(object sender, EventArgs e)
        {
        }

        private void lstFileNames_SelectedValueChanged(object sender, EventArgs e)
        {
            txtSelCount.Text = lstFileNames.SelectedItems.Count.ToString();

            if (!lstFileNames.SelectedItems.IsNull())
                mnuOpenFile.Enabled = (lstFileNames.SelectedItems.Count == 1);
            else mnuOpenFile.Enabled = false;
        }

        /// <summary>
        /// Resizes fields against content for both Item and Selection counts
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void txtItemCount_TextChanged(object sender, EventArgs e)
        {
            TextBox txtItemCount = (TextBox)sender;
            txtItemCount.Size = Utilities.AutoResizeTextBox(txtItemCount);
        }

        private void btnDelete_MouseEnter(object sender, EventArgs e)
        {
            if (lstFileNames.Items.Count > 0)
                if (lstFileNames.SelectedItems.Count > 0)
                    btnDelete.Text = DeleteButtonText + " (" + lstFileNames.SelectedItems.Count + ")";
        }

        private void btnDelete_MouseLeave(object sender, EventArgs e)
        {
            btnDelete.Text = DeleteButtonText;
        }

        private void chkAutoUpdate_CheckStateChanged(object sender, EventArgs e)
        {
            if (chkAutoUpdate.Checked) EvaluateManualFileDetailsUpdate(); else btnUpdateDetails.Enabled = false;

            if (chkAutoUpdate.CheckState == CheckState.Checked)
                btnUpdateDetails_Click(sender, e);
        }

        private void EvaluateManualFileDetailsUpdate()
        {
            if (!txtId.Text.IsNullOrEmpty())
            {
                if (txtId.Text.IsNumeric())
                {
                    int fileId = Convert.ToInt32(txtId.Text);
                    FileDetails fileDetails = GetValidFileList(false).Where(fdet => fdet.FileId == fileId).FirstOrDefault();

                    btnUpdateDetails.Enabled = fileDetails.IsNull() || chkAutoUpdate.Checked ? false
                        : (!chkAutoUpdate.Checked && (fileDetails.TakeSuggested != radSuggested.Checked || fileDetails.NameCustom != txtNameCustom.Text));
                }
            }
        }

        private void mnuRevertUpdates_Click(object sender, EventArgs e)
        {
            //if (Program.IsDebug) LastBatchStamp = "20130512_025400"; // { LastBatchStamp = (Utilities.GetControlName(sender) == mnuRevertFromLog.Name) ? "20130512_025400" : ""; } RKD

            bool prompt = false;
            bool isNoLogSelected = SelectedLogFile.IsNullOrEmpty() || File.GetAttributes(SelectedLogFile).HasFlag(FileAttributes.Directory);

            if (Utilities.GetControlName(sender) == mnuRevertLastBatch.Name)
            {
                if (LastBatchStamp.IsNullOrEmpty())
                {
                    if (MessageBoxUtil.Show(Program.IsDebug, "Last batch not available!" + Utilities.cNewLine + Utilities.cNewLine + "Click Yes to select from log?" + (isNoLogSelected ? Utilities.cNewLine + "(Log file not selected. Select now?)" : ""), "Update reversal confirmation..", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
                    { if (isNoLogSelected) prompt = true; }
                    else return; // mnuRevertFromLog_Click(sender, e);
                }
            }

            if (Utilities.GetControlName(sender) == mnuRevertFromLog.Name)
            {
                LastBatchStamp = "";
                if (isNoLogSelected)
                {
                    if (MessageBoxUtil.Show(Program.IsDebug, ResourceBulkRenamer.Msg_LogNotSelected, ResourceBulkRenamer.App_LogNotSelected, MessageBoxButtons.YesNo) == DialogResult.Yes)
                        prompt = true;
                    else
                        return;
                }
                else
                    prompt = false;
            }

            // Read log file
            OpenFileDialog dlgOpenLogFile = null;
            if (prompt)
                if (BrowseLogFile(ref dlgOpenLogFile) != DialogResult.OK)
                    return;

            string logPath = prompt ? dlgOpenLogFile.FileName : SelectedLogFile;
            DataTable dtLog = Utilities.ReadCSV(logPath, tableName: "LogDetails");

            if (dtLog.Rows.Count == 0) return;

            DataRow[] drLogs = null;
            ObservableCollection<LogDetails> logDetails = new ObservableCollection<LogDetails>();

            if (Utilities.GetControlName(sender) == mnuRevertLastBatch.Name && !LastBatchStamp.IsNullOrEmpty())
                drLogs = dtLog.Select("Batch = '" + LastBatchStamp + "' AND Status = 'Successful'", "Old_Name ASC");
            else if (Utilities.GetControlName(sender) == mnuRevertFromLog.Name || LastBatchStamp.IsNullOrEmpty())
            {
                drLogs = dtLog.Rows.Cast<DataRow>().ToArray();

                //this.Opacity = 0.75;
                logDetails = GetFileListFromDatatable(drLogs);
                frmBatchSelector frmBatchSelector = new frmBatchSelector(logDetails);
                frmBatchSelector.ShowDialog(this);

                //this.Opacity = 1;
                if (frmBatchSelector.DialogResult == DialogResult.OK) LastBatchStamp = frmBatchSelector.SelectedBatchStamp;

                frmBatchSelector.Dispose();
                frmBatchSelector = null;

                if (!LastBatchStamp.IsNullOrEmpty()) { mnuRevertUpdates_Click(mnuRevertLastBatch, e); return; }
                else { MessageBoxUtil.Show(Program.IsDebug, "No batch selected!", "Revert Update confirmation..", MessageBoxButtons.OK, MessageBoxIcon.Information); return; }
            }

            if (!drLogs.Any()) return;

            AllFileDetails = logDetails.AddCollectionIndex<FileDetails>("FileId").ToObservableCollection();
            grpOutput.Enabled = radCustom.Enabled = radSuggested.Enabled = false;
            ReloadList(true);

            LastBatchStamp = LastBatchStamp_bup;
        }

        private ObservableCollection<LogDetails> GetFileListFromDatatable(DataRow[] drLogs)
        {
            //Batch,Time_stamp,Base_Dir,Old_Name,New_Name,Extn,Status

            ObservableCollection<LogDetails> allFileDetails = new ObservableCollection<LogDetails>();

            drLogs.ForEach(dr => allFileDetails.Add(
                new LogDetails
                {
                    _FullPath = dr["Base_Dir"].ToString() + Path.DirectorySeparatorChar + dr["New_Name"].ToString().RestoreCSVChars(),
                    _NameOrg = dr["New_Name"].ToString().RestoreCSVChars(),
                    _FileExtn = dr["Extn"].ToString(),

                    NameCustom = dr["Old_Name"].ToString().RestoreCSVChars(),
                    TakeSuggested = false,

                    // Log details
                    BatchStamp = dr["Batch"].ToString(),
                    TimeStamp = Utilities.GetDateTimeFromFormat(dr["Time_stamp"].ToString(), ResourceBulkRenamer.Format_LogRecTimestamp), // Convert.ToDateTime(dr["Time_stamp"]),
                    Status = dr["Status"].ToString().RestoreCSVChars(),
                    Description = dr["Batch_Desc"].ToString().RestoreCSVChars()
                }));

            allFileDetails.AddCollectionIndex<LogDetails>("FileId");
            allFileDetails._ForEach(fdet => fdet.NameSuggested = "N/A"); // "SuggestionNotReqd_File#" + fdet.FileId);

            return allFileDetails;
        }

        private DialogResult BrowseLogFile(ref OpenFileDialog dlgOpenLogFile)
        {
            dlgOpenLogFile = new OpenFileDialog
               {
                   Title = ResourceBulkRenamer.Title_LogFileSelection,
                   DefaultExt = "csv",
                   Multiselect = false,
                   AddExtension = true,
                   CheckFileExists = false,
                   CheckPathExists = false,
                   RestoreDirectory = true
               };

            string sDefaultLogPath = sDefaultPath + "\\Logs";
            dlgOpenLogFile.Filter = ResourceBulkRenamer.Filter_LogFileSelection;
            dlgOpenLogFile.InitialDirectory = !SelectedLogFile.IsPathFormatValid() ? sDefaultLogPath
                : Directory.Exists(Path.GetDirectoryName(SelectedLogFile)) ? SelectedLogFile : sDefaultLogPath;
            dlgOpenLogFile.FileName = File.Exists(SelectedLogFile) ? SelectedLogFile : "log_" + DateTime.Now.ToString(ResourceBulkRenamer.Format_LogFileTimestamp);

            return dlgOpenLogFile.ShowDialog();
        }

        private void picGridPreview_Click(object sender, EventArgs e)
        {
            frmGridPreview frmGridPreview = new frmGridPreview(
                (ObservableCollection<FileDetails>)lstFileNames.DataSource, false);
            frmGridPreview.ShowDialog(this);

            if (frmGridPreview.DialogResult == DialogResult.OK)
            {
                //TRY_two_ConvertAll_in_SINGLE_query;
                //CHANGE_DESIGN_use_invisible_CHECKBOX_for_TakeSuggested;
                //TRY_autoupdate_TakeSuggested_in_MODEL;

                // Update TakeSuggested
                AllFileDetails.Where(fdet => frmGridPreview.AllFileDetails_mod.Select(dup => dup.FileId).Contains(fdet.FileId)).ToList()
                   ._ForEach(fdet => fdet.TakeSuggested
                       = frmGridPreview.AllFileDetails_mod.Where(dup => dup.FileId == fdet.FileId).First().TakeSuggested);
                // Update NameCustom
                AllFileDetails.Where(fdet => frmGridPreview.AllFileDetails_mod.Select(dup => dup.FileId).Contains(fdet.FileId)).ToList()
                    ._ForEach(fdet => fdet.NameCustom
                        = frmGridPreview.AllFileDetails_mod.Where(dup => dup.FileId == fdet.FileId).First().NameCustom);
                // AllFileDetails = AllFileDetails_dup;
                ReloadList(false);

                if (AllFileDetails.IsLinqIterable<FileDetails>(true))
                {
                    if (!txtId.Text.IsNullOrEmpty())
                        if (txtId.Text.IsNumeric())
                            LoadFileDetails(AllFileDetails.IndexOf(AllFileDetails.Where(fdet => fdet.FileId == Convert.ToInt32(txtId.Text)).First()));
                        else LoadFileDetails(0);
                    else LoadFileDetails(0);
                }
                else ClearEditor();
            }

            frmGridPreview.Dispose();
            frmGridPreview = null;
        }

        private void mnuLaunchSampler_Click(object sender, EventArgs e)
        {
            BulkRenamer.Sampler.frmSampler frmSampler;

            if (Form.ModifierKeys == Keys.Control)
                frmSampler = new BulkRenamer.Sampler.frmSampler(Program.IsDebug, SelectedBasePath);
            else
                frmSampler = new BulkRenamer.Sampler.frmSampler(Program.IsDebug, txtBasePath.Text, ((ObservableCollection<FileDetails>)lstFileNames.DataSource)._Select(fdet => Path.GetFileName(fdet.FullPath)));

            frmSampler.StartPosition = FormStartPosition.CenterParent;
            frmSampler.ShowInTaskbar = false;
            frmSampler.ShowDialog(this);

            frmSampler.Close();
            frmSampler.Dispose();
        }

        private void mnuQuit_Click(object sender, EventArgs e)
        {
            switch (MessageBoxUtil.Show(null, ResourceBulkRenamer.Msg_QuitApp, ResourceBulkRenamer.App_QuitApp, MessageBoxButtons.YesNo, MessageBoxIcon.Question))
            {
                case DialogResult.Yes:
                    Application.Exit();
                    break;
                case DialogResult.No:
                default:
                    break;
            }
        }

        private void mnuOpenFile_Click(object sender, EventArgs e)
        {
            if (!lstFileNames.SelectedItem.IsNull())
                Utilities.OpenPath(((FileDetails)lstFileNames.SelectedItem).FullPath);
        }

        /// <summary>
        /// Deletes file from disk and application
        /// </summary>
        private void mnuDeleteFile_Click(object sender, EventArgs e)
        {
            if (!lstFileNames.SelectedItems.IsNull())
            {
                Dictionary<int, string> dict = new Dictionary<int, string>();
                lstFileNames.SelectedItems.Cast<FileDetails>().ForEach(fdet => dict.AddToDictionary(fdet.FileId, fdet.FullPath));

                dict._ForEach(pair => File.Delete(pair.Value)); // Delete from disk
                btnDelete_Click(sender, e); // Delete from file list
            }
        }

        private void mnuShowInTray_Click(object sender, EventArgs e)
        {
            ntfySysTray.Visible = mnuShowInTray.Checked;
        }

        private void cmbUpdateType_SelectedIndexChanged(object sender, EventArgs e)
        {
            mnuUpdateFiles.Text = btnUpdateFiles.Text
                = "{0} {1}".FormatTextFromParams(sUpdateFiles, cmbUpdateType.Text.TrimAndTake(5, eTrimDirection.Left, false).ToLower() + "s").Trim();
            MessageBoxUtil.Show(Program.IsDebug, "YET TO BE IMPLEMENTED..");
        }

        private void mnuAppend_Click(object sender, EventArgs e)
        {
            txtAppend.Focus();
        }

        private void mnuReplace_Click(object sender, EventArgs e)
        {
            txtReplace.Focus();
        }
    }
}

/* Create GENERIC
private void AllFileDetails_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
    if (e.Action == NotifyCollectionChangedAction.Add)
    {
        int lastIndex = Utilities.IsLinqIterable<FileDetails>(AllFileDetails, true)
            ? AllFileDetails.Select(p => p.FileId).Max() : 0; // PropertyCollection.Last().PropertySort
        foreach (FileDetails fdet in e.NewItems)
        {
            lastIndex++;
            fdet.FileId = lastIndex;
        }
    }
}
*/

/*
 * export to excel in CSV
 * OR
 * create excel in loop
 * 
 * 
 * 
 * 
 * 
 * MSWord: http://msdn.microsoft.com/en-us/library/system.windows.forms.filedialog.defaultext.aspx
 * 
 * 
 * 
 * 
 * 
 * (string)ConfigurationSettings.AppSettings["RenameBatch"]);
 * 
 * 
 * 
 * 
 * 
 * myListBox.BeginUpdate();
 * myListBox.DataSource = myList;
 * myListBox.EndUpdate();
 * 
 * 
 * 
 * 
 * // lstFileNames.Items.Cast<ObservableCollection<FileDetails>>();
 * // lost foc > got foc
*/
